Skip to content

Commit 0eafb48

Browse files
feat: add proc builder payload flow
Assisted-by: openai/gpt-5.4 on opencode Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
1 parent 0276bbc commit 0eafb48

22 files changed

+1629
-31
lines changed

data/json/main.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ local voltmeter = require("./voltmeter")
22
local slimepit = require("./slimepit")
33
local artifact_analyzer = require("./artifact_analyzer")
44
local lua_traits = require("./lua_traits")
5+
local procgen = require("./proc")
56

67
local mod = game.mod_runtime[game.current_mod]
78
local storage = game.mod_storage[game.current_mod]
@@ -11,3 +12,4 @@ mod.slimepit = slimepit
1112
mod.artifact_analyzer = artifact_analyzer
1213
mod.lua_traits = lua_traits
1314
lua_traits.register(mod)
15+
mod.procgen = procgen

data/json/proc.lua

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
local proc = {}
2+
proc.food = {}
3+
4+
local function sum_parts(parts, key)
5+
local total = 0
6+
for _, part in ipairs(parts) do
7+
total = total + (part[key] or 0)
8+
end
9+
return total
10+
end
11+
12+
local function sum_vitamins(parts)
13+
local total = {}
14+
for _, part in ipairs(parts) do
15+
for id, value in pairs(part.vit or {}) do
16+
total[id] = (total[id] or 0) + value
17+
end
18+
end
19+
return total
20+
end
21+
22+
local function has_tag(parts, needle)
23+
for _, part in ipairs(parts) do
24+
for _, tag in ipairs(part.tag or {}) do
25+
if tag == needle then return true end
26+
end
27+
end
28+
return false
29+
end
30+
31+
function proc.food.full(params)
32+
local facts = params.facts or {}
33+
local mass_g = sum_parts(facts, "mass_g")
34+
local volume_ml = sum_parts(facts, "volume_ml")
35+
local kcal = sum_parts(facts, "kcal")
36+
local vit = sum_vitamins(facts)
37+
local name = "sandwich"
38+
if has_tag(facts, "meat") then
39+
name = "meat sandwich"
40+
elseif has_tag(facts, "cheese") then
41+
name = "cheese sandwich"
42+
elseif has_tag(facts, "veg") then
43+
name = "veggy sandwich"
44+
end
45+
return {
46+
mass_g = mass_g,
47+
volume_ml = volume_ml,
48+
kcal = kcal,
49+
vit = vit,
50+
name = name,
51+
}
52+
end
53+
54+
function proc.food.make(params)
55+
local blob = params.blob or {}
56+
return {
57+
name = blob.name,
58+
kcal = blob.kcal,
59+
mass_g = blob.mass_g,
60+
volume_ml = blob.volume_ml,
61+
mode = "none",
62+
}
63+
end
64+
65+
return proc

data/json/proc/sandwich.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[
2+
{
3+
"type": "PROC",
4+
"id": "sandwich",
5+
"cat": "food",
6+
"res": "sandwich_generic",
7+
"hist": {
8+
"def": "none",
9+
"ok": [ "none", "compact", "full" ]
10+
},
11+
"slot": [
12+
{ "id": "top", "role": "bread", "min": 1, "max": 1, "ok": [ "itype:bread", "itype:bread_garlic" ] },
13+
{ "id": "bot", "role": "bread", "min": 1, "max": 1, "ok": [ "itype:bread", "itype:bread_garlic" ] },
14+
{ "id": "veg", "role": "veg", "min": 0, "max": 4, "rep": true, "ok": [ "itype:lettuce", "itype:tomato" ] },
15+
{ "id": "cheese", "role": "cheese", "min": 0, "max": 2, "rep": true, "ok": [ "itype:cheese", "itype:cheese_fresh", "itype:cheese_hard" ] },
16+
{ "id": "cond", "role": "cond", "min": 0, "max": 2, "rep": true, "ok": [ "itype:mustard" ] },
17+
{ "id": "meat", "role": "meat", "min": 0, "max": 3, "rep": true, "ok": [ "itype:meat_cooked", "itype:meat_cooked_seasoned" ] }
18+
],
19+
"lua": {
20+
"full": "proc.food.full",
21+
"make": "proc.food.make"
22+
}
23+
}
24+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"type": "recipe",
4+
"result": "sandwich_generic",
5+
"category": "CC_FOOD",
6+
"subcategory": "CSC_FOOD_MEAT",
7+
"skill_used": "cooking",
8+
"difficulty": 1,
9+
"time": 100,
10+
"proc": true,
11+
"proc_id": "sandwich",
12+
"builder_name": { "str": "Sandwich" },
13+
"builder_desc": { "str": "Pick bread and fillings." },
14+
"qualities": [],
15+
"tools": [],
16+
"components": []
17+
}
18+
]

src/consumption.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "mutation.h"
3838
#include "options.h"
3939
#include "pldata.h"
40+
#include "proc_item.h"
4041
#include "recipe.h"
4142
#include "recipe_dictionary.h"
4243
#include "requirements.h"
@@ -310,6 +311,15 @@ static std::map<vitamin_id, int> compute_default_effective_vitamins(
310311
static nutrients compute_default_effective_nutrients( const item &comest,
311312
const Character &you, const cata::flat_set<flag_id> &extra_flags = {} )
312313
{
314+
if( const auto kcal = proc::blob_kcal( comest ) ) {
315+
auto ret = nutrients{};
316+
ret.kcal = *kcal;
317+
if( const auto vit = proc::blob_vitamins( comest ) ) {
318+
ret.vitamins = *vit;
319+
}
320+
return ret;
321+
}
322+
313323
return { compute_default_effective_kcal( comest, you, extra_flags ),
314324
compute_default_effective_vitamins( comest, you ) };
315325
}

src/crafting.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
#include "pimpl.h"
5656
#include "player.h"
5757
#include "player_activity.h"
58+
#include "proc_item.h"
59+
#include "popup.h"
5860
#include "point.h"
5961
#include "recipe.h"
6062
#include "recipe_dictionary.h"
@@ -605,6 +607,12 @@ void Character::make_craft_with_command( const recipe_id &id_to_make, int batch_
605607
return;
606608
}
607609

610+
if( recipe_to_make.is_proc() ) {
611+
popup( _( "Procedural builder stub for %s" ), recipe_to_make.builder_name().translated().empty() ?
612+
recipe_to_make.result_name() : recipe_to_make.builder_name().translated() );
613+
return;
614+
}
615+
608616
*last_craft = craft_command( &recipe_to_make, batch_size, is_long, this, loc );
609617
last_craft->execute();
610618
}
@@ -2209,6 +2217,14 @@ void crafting::complete_disassemble( Character &who, const iuse_location &target
22092217
// Even if the best-fit recipe does not involve those items
22102218
location_vector<item> &components = dis_item.get_components();
22112219

2220+
if( const auto payload = proc::read_payload( dis_item ); payload &&
2221+
payload->mode == proc::hist::compact ) {
2222+
auto restored = proc::restore_parts( *payload );
2223+
std::ranges::for_each( restored, [&]( detached_ptr<item> &entry ) {
2224+
components.push_back( std::move( entry ) );
2225+
} );
2226+
}
2227+
22122228
// If the components are empty, item is the default kind and made of default components
22132229
if( components.empty() ) {
22142230
const bool uncraft_liquids_contained = dis.has_flag( flag_UNCRAFT_LIQUIDS_CONTAINED );

src/item.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
#include "overmapbuffer.h"
8686
#include "pimpl.h"
8787
#include "player.h"
88+
#include "proc_item.h"
8889
#include "player_activity.h"
8990
#include "pldata.h"
9091
#include "point.h"
@@ -4956,6 +4957,7 @@ void item::on_contents_changed()
49564957

49574958
void item::on_damage( int qty, damage_type )
49584959
{
4960+
proc::apply_on_damage( *this, qty );
49594961
if( is_corpse() && qty + damage_ >= max_damage() ) {
49604962
set_flag( flag_PULPED );
49614963
}
@@ -5467,6 +5469,14 @@ units::mass item::weight( bool include_contents, bool integral ) const
54675469
return ret;
54685470
}
54695471

5472+
if( const auto mass = proc::blob_mass( *this ) ) {
5473+
auto ret = units::from_gram( *mass );
5474+
if( include_contents ) {
5475+
ret += contents.item_weight_modifier();
5476+
}
5477+
return ret;
5478+
}
5479+
54705480
units::mass ret;
54715481
std::string local_str_mass = integral ? get_var( "integral_weight" ) : get_var( "weight" );
54725482
if( local_str_mass.empty() ) {
@@ -5594,6 +5604,10 @@ units::volume item::base_volume() const
55945604
}
55955605
}
55965606

5607+
if( const auto proc_volume = proc::blob_volume( *this ) ) {
5608+
return units::from_milliliter( *proc_volume );
5609+
}
5610+
55975611
return type->volume;
55985612
}
55995613

@@ -5615,6 +5629,10 @@ units::volume item::volume( bool integral ) const
56155629
return ret;
56165630
}
56175631

5632+
if( const auto proc_volume = proc::blob_volume( *this ) ) {
5633+
return units::from_milliliter( *proc_volume );
5634+
}
5635+
56185636
const int local_volume = get_var( "volume", -1 );
56195637
units::volume ret;
56205638
if( local_volume >= 0 ) {
@@ -10015,6 +10033,10 @@ std::string item::components_to_string() const
1001510033

1001610034
uint64_t item::make_component_hash() const
1001710035
{
10036+
if( const auto hash = proc::component_hash( *this ) ) {
10037+
return *hash;
10038+
}
10039+
1001810040
// First we need to sort the IDs so that identical ingredients give identical hashes.
1001910041
std::multiset<std::string> id_set;
1002010042
for( const item * const &it : components ) {
@@ -11322,6 +11344,21 @@ int item::get_min_str() const
1132211344
std::vector<item_comp> item::get_uncraft_components() const
1132311345
{
1132411346
std::vector<item_comp> ret;
11347+
if( const auto payload = proc::read_payload( *this ); payload &&
11348+
payload->mode == proc::hist::compact ) {
11349+
std::ranges::for_each( payload->parts, [&]( const proc::compact_part & part ) {
11350+
auto iter = std::ranges::find_if( ret, [&]( item_comp & obj ) {
11351+
return obj.type == part.id;
11352+
} );
11353+
if( iter != ret.end() ) {
11354+
iter->count += part.n;
11355+
} else {
11356+
ret.emplace_back( part.id, part.n );
11357+
}
11358+
} );
11359+
return ret;
11360+
}
11361+
1132511362
if( components.empty() ) {
1132611363
//If item wasn't crafted with specific components use default recipe
1132711364
std::vector<std::vector<item_comp>> recipe = recipe_dictionary::get_uncraft(

0 commit comments

Comments
 (0)