Skip to content

Commit

Permalink
perf: make more items stackable (#3489)
Browse files Browse the repository at this point in the history
* fix: allow using charges when sewing

* feat: item to stackable converter script

* perf: make stuff stackable

rag was also made GENERIC because TOOL worked poorly

* fix: account for count by charges item

* refactor: use `std::clamp`

* fix: remove fuel randomness for charge based items

this makes fire source calculation flaky.
  • Loading branch information
scarf005 authored Dec 25, 2023
1 parent 8b9180d commit 8870a9e
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 54 deletions.
7 changes: 1 addition & 6 deletions data/json/items/comestibles/med.json
Original file line number Diff line number Diff line change
Expand Up @@ -1751,17 +1751,12 @@
{
"id": "disinrag",
"type": "COMESTIBLE",
"copy-from": "rag_abstract",
"comestible_type": "MED",
"name": { "str": "antiseptic soaked rag" },
"description": "A rag soaked in antiseptic. Useful for light wounds, probably won't help with deep bites.",
"weight": "80 g",
"//": "Can't copy-from rag, it breaks the stacking for some reason!",
"volume": "250 ml",
"price": "250 cent",
"price_postapoc": "50 cent",
"material": "cotton",
"symbol": ",",
"color": "white",
"flags": [ "NO_INGEST" ],
"use_action": {
"type": "heal",
Expand Down
58 changes: 44 additions & 14 deletions data/json/items/generic.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@
"flags": [ "NO_SALVAGE" ],
"weight": "114 g",
"volume": "250 ml",
"category": "spare_parts"
"category": "spare_parts",
"stackable": true
},
{
"type": "GENERIC",
Expand All @@ -223,7 +224,8 @@
"weight": "100 g",
"volume": "250 ml",
"category": "spare_parts",
"to_hit": -1
"to_hit": -1,
"stackable": true
},
{
"type": "GENERIC",
Expand All @@ -239,7 +241,8 @@
"weight": "100 g",
"volume": "250 ml",
"category": "spare_parts",
"to_hit": -1
"to_hit": -1,
"stackable": true
},
{
"type": "GENERIC",
Expand All @@ -254,7 +257,8 @@
"material": [ "nomex" ],
"flags": [ "NO_SALVAGE" ],
"weight": "42 g",
"volume": "250 ml"
"volume": "250 ml",
"stackable": true
},
{
"type": "GENERIC",
Expand All @@ -270,7 +274,8 @@
"weight": "45 g",
"volume": "250 ml",
"to_hit": -2,
"flags": [ "UNRECOVERABLE" ]
"flags": [ "UNRECOVERABLE" ],
"stackable": true
},
{
"type": "GENERIC",
Expand Down Expand Up @@ -623,7 +628,8 @@
"material": "steel",
"weight": "151 g",
"volume": "500 ml",
"to_hit": -2
"to_hit": -2,
"stackable": true
},
{
"type": "GENERIC",
Expand All @@ -637,7 +643,8 @@
"material": "steel",
"weight": "302 g",
"volume": "500 ml",
"to_hit": -2
"to_hit": -2,
"stackable": true
},
{
"type": "GENERIC",
Expand Down Expand Up @@ -669,7 +676,8 @@
"volume": "1500 ml",
"bashing": 12,
"to_hit": -1,
"qualities": [ [ "HAMMER", 1 ] ]
"qualities": [ [ "HAMMER", 1 ] ],
"stackable": true
},
{
"type": "GENERIC",
Expand Down Expand Up @@ -1677,16 +1685,37 @@
"to_hit": -6
},
{
"abstract": "rag_abstract",
"type": "GENERIC",
"id": "rag_bloody",
"name": { "str": "rag" },
"description": "This is a largish piece of cloth, useful in crafting and possibly for staunching bleeding. It's a bug if you see this.",
"category": "spare_parts",
"weight": "80 g",
"volume": "250 ml",
"price": 0,
"price_postapoc": 0,
"material": "cotton",
"symbol": ",",
"color": "white",
"flags": [ "NO_SALVAGE" ],
"stackable": true
},
{
"id": "rag",
"type": "GENERIC",
"name": { "str": "rag" },
"description": "This is a largish piece of cloth, useful in crafting and possibly for staunching bleeding.",
"copy-from": "rag_abstract",
"use_action": [ { "type": "heal", "move_cost": 200, "used_up_item": "rag_bloody", "bleed": 0.5, "limb_power": 0 }, "WASH_HARD_ITEMS" ]
},
{
"type": "GENERIC",
"id": "rag_bloody",
"copy-from": "rag_abstract",
"color": "red",
"name": { "str": "blood soaked rag" },
"description": "A large rag, drenched in blood. It could be cleaned with boiling water.",
"material": "cotton",
"flags": [ "NO_SALVAGE", "TRADER_AVOID" ],
"weight": "80 g",
"volume": "250 ml"
"extend": { "flags": "TRADER_AVOID" }
},
{
"id": "pipe_cleaner",
Expand Down Expand Up @@ -2651,7 +2680,8 @@
"flags": [ "NO_SALVAGE" ],
"weight": "80 g",
"volume": "250 ml",
"category": "spare_parts"
"category": "spare_parts",
"stackable": true
},
{
"id": "cerberus_laser",
Expand Down
1 change: 1 addition & 0 deletions data/json/items/generic/string.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
{
"id": "string_6",
"type": "GENERIC",
"stackable": true,
"category": "spare_parts",
"name": { "str": "short string" },
"description": "A 6-inch (or about 15 cm) long piece of cotton string.",
Expand Down
3 changes: 2 additions & 1 deletion data/json/items/resources/misc.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
"bashing": 5,
"cutting": 2,
"to_hit": -1,
"qualities": [ [ "BUTCHER", -66 ] ]
"qualities": [ [ "BUTCHER", -66 ] ],
"stackable": true
},
{
"id": "fighter_sting",
Expand Down
10 changes: 6 additions & 4 deletions data/json/items/resources/plastic.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[
{
"id": "plastic_chunk",
"type": "TOOL",
"type": "GENERIC",
"category": "spare_parts",
"name": { "str": "plastic chunk" },
"description": "This is a piece of plastic. It could be used to fabricate, repair, or reinforce plastic items.",
Expand All @@ -12,11 +12,12 @@
"material": "plastic",
"symbol": ",",
"color": "light_blue",
"flags": [ "NO_SALVAGE" ]
"flags": [ "NO_SALVAGE" ],
"stackable": true
},
{
"id": "nylon",
"type": "TOOL",
"type": "GENERIC",
"category": "spare_parts",
"name": { "str_sp": "synthetic fabric" },
"description": "This is small bolt of synthetic fabric. Unlike you and other natural materials, it won't degrade much with age. Maybe that's less of a bad thing now.",
Expand All @@ -27,7 +28,8 @@
"material": "nylon",
"symbol": ",",
"color": "white",
"flags": [ "NO_SALVAGE" ]
"flags": [ "NO_SALVAGE" ],
"stackable": true
},
{
"id": "plastic_sheet",
Expand Down
9 changes: 6 additions & 3 deletions data/json/items/resources/wood.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"bashing": 4,
"to_hit": 1,
"qualities": [ [ "COOK", 1 ] ],
"flags": [ "NO_SALVAGE", "TRADER_AVOID", "FIREWOOD" ]
"flags": [ "NO_SALVAGE", "TRADER_AVOID", "FIREWOOD" ],
"stackable": true
},
{
"id": "stick",
Expand Down Expand Up @@ -102,7 +103,8 @@
"bashing": 10,
"price": "10 USD",
"price_postapoc": "10 cent",
"flags": [ "FIREWOOD" ]
"flags": [ "FIREWOOD" ],
"stackable": true
},
{
"type": "GENERIC",
Expand Down Expand Up @@ -140,7 +142,8 @@
"bashing": 8,
"price": "80 USD",
"price_postapoc": "10 cent",
"flags": [ "FIREWOOD" ]
"flags": [ "FIREWOOD" ],
"stackable": true
},
{
"type": "GENERIC",
Expand Down
16 changes: 0 additions & 16 deletions data/json/items/tool/toiletries.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,6 @@
"techniques": "WBLOCK_1",
"use_action": "MOP"
},
{
"id": "rag",
"type": "TOOL",
"category": "spare_parts",
"name": { "str": "rag" },
"description": "This is a largish piece of cloth, useful in crafting and possibly for staunching bleeding.",
"weight": "80 g",
"volume": "250 ml",
"price": 0,
"price_postapoc": 0,
"material": "cotton",
"symbol": ",",
"color": "white",
"use_action": [ { "type": "heal", "move_cost": 200, "used_up_item": "rag_bloody", "bleed": 0.5, "limb_power": 0 }, "WASH_HARD_ITEMS" ],
"flags": [ "NO_SALVAGE" ]
},
{
"type": "GENERIC",
"category": "tools",
Expand Down
71 changes: 71 additions & 0 deletions scripts/to_stackable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { z } from "https://deno.land/x/catjazz@v0.0.2/mod.ts"
import { Command } from "https://deno.land/x/catjazz@v0.0.2/deps/cliffy.ts"
import { cliOptions } from "https://deno.land/x/catjazz@v0.0.2/utils/cli.ts"
import { timeit } from "https://deno.land/x/catjazz@v0.0.2/utils/timeit.ts"
import {
applyRecursively,
schemaTransformer,
} from "https://deno.land/x/catjazz@v0.0.2/utils/transform.ts"
import {
type CataEntry,
Entry,
parseCataJson,
readRecursively,
} from "https://deno.land/x/catjazz@v0.0.2/utils/parse.ts"
import { fmtJsonRecursively } from "https://deno.land/x/catjazz@v0.0.2/utils/json_fmt.ts"
import { match, P } from "https://deno.land/x/catjazz@v0.0.2/deps/ts_pattern.ts"
import { id } from "https://deno.land/x/catjazz@v0.0.2/utils/id.ts"

// FIXME: include in library
const unpack = (xs: string[] | Entry[]) =>
match(xs)
.with(P.array(P.string), id)
.otherwise((xs) => xs.map(({ path }) => path))

const main = new Command()
// TODO: allow multiple paths
.option(...cliOptions.path)
.option(...cliOptions.format)
.option(...cliOptions.quiet)
.arguments("<...ids>")
.description("Converts given id to stackable.")
.action(async ({ path, quiet = false, format }, ...ids) => {
const timeIt = timeit(quiet)

const schema = z.object({
// @ts-expect-error: zod hates string literal mapping
id: z.union(ids.map((id) => z.literal(id))) as z.ZodString,
type: z.string().transform((x) => x === "TOOL" ? "GENERIC" : x),
})
.passthrough()
.transform(({ id, ...rest }) => {
console.log(id)
return { id, ...rest, stackable: true }
})

const transformer = schemaTransformer(schema)
const ignore = (entries: CataEntry[]) =>
entries.find(({ type }) => ["mapgen", "palette", "mod_tileset"].includes(type))

// FIXME: include in library
const mapgenIgnoringTransformer = (text: string) => {
const entries = parseCataJson(text)
return ignore(entries) ? text : JSON.stringify(transformer(entries), null, 2)
}

const recursiveTransformer = applyRecursively(mapgenIgnoringTransformer)

const entries = await timeIt({ name: "reading JSON", val: readRecursively(path) })

await timeIt({ name: "Transforming", val: recursiveTransformer(entries) })

if (!format) return
await timeIt({
name: "formatting",
val: fmtJsonRecursively({ formatterPath: format, quiet: true })(unpack(entries)),
})
})

if (import.meta.main) {
await main.parse(Deno.args)
}
2 changes: 1 addition & 1 deletion src/activity_item_handling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3381,7 +3381,7 @@ void try_fuel_fire( player_activity &act, player &p, const bool starting_fire )
}
}
if( found ) {
int quantity = std::max( 1, std::min( found->charges, found->charges_per_volume( 250_ml ) ) );
const int quantity = std::clamp( found->charges, 1, found->charges_per_volume( 250_ml ) );
// Note: move_item() handles messages (they're the generic "you drop x")
move_item( p, *found, quantity, *refuel_spot, *best_fire );
}
Expand Down
2 changes: 1 addition & 1 deletion src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8297,7 +8297,7 @@ float item::simulate_burn( fire_data &frd ) const
}

if( count_by_charges() ) {
int stack_burnt = rng( type->stack_size / 2, type->stack_size );
const int stack_burnt = type->stack_size;
time_added *= stack_burnt;
smoke_added *= stack_burnt;
burn_added *= stack_burnt;
Expand Down
25 changes: 17 additions & 8 deletions src/iuse_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3767,20 +3767,26 @@ int heal_actor::finish_using( player &healer, player &patient, item &it, hp_part
}
}

const auto copy_flags = [&]( item & it ) {
for( const auto &flag : used_up_item_flags ) {
it.set_flag( flag );
}
};

// TODO: make this less cursed
if( !used_up_item_id.is_empty() ) {
// If the item is a tool, `make` it the new form
// Otherwise it probably was consumed, so create a new one
if( it.is_tool() ) {
if( it.is_tool() || ( it.count_by_charges() && it.charges <= used_up_item_charges ) ) {
it.convert( used_up_item_id );
for( const auto &flag : used_up_item_flags ) {
it.set_flag( flag );
}
copy_flags( it );
} else {
if( it.count_by_charges() && it.charges > used_up_item_charges ) {
it.charges -= used_up_item_charges;
}
item *used_up = item::spawn_temporary( used_up_item_id, it.birthday() );
used_up->charges = used_up_item_charges;
for( const auto &flag : used_up_item_flags ) {
used_up->set_flag( flag );
}
copy_flags( *used_up );
for( int count = 0; count < used_up_item_quantity; count++ ) {
healer.i_add_or_drop( item::spawn( *used_up ) );
}
Expand Down Expand Up @@ -4787,7 +4793,10 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const
const inventory &crafting_inv = p.crafting_inventory();
// Go through all discovered repair items and see if we have any of them available
for( auto &cm : clothing_mods::get_all() ) {
has_enough[cm.item_string] = crafting_inv.has_amount( cm.item_string, items_needed );
has_enough[cm.item_string] =
item::count_by_charges( cm.item_string )
? crafting_inv.has_charges( cm.item_string, items_needed )
: crafting_inv.has_amount( cm.item_string, items_needed );
}

int mod_count = 0;
Expand Down

0 comments on commit 8870a9e

Please sign in to comment.