Smithing Expansion: Knives and Mothsilk (#31084)

* Smith Knives Initial Commit

* sprites and handling

* Knife stuff, mothsilk

* Touch ups

* Linters

* Apply suggestions from code review

Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>

* Abstract type desc

---------

Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
This commit is contained in:
PollardTheDragon
2025-12-08 16:34:30 -05:00
committed by GitHub
parent 2517adf705
commit 5d4a01148e
13 changed files with 419 additions and 11 deletions
@@ -49,6 +49,7 @@
var/list/innate_actions = list(
/datum/action/innate/nian_caterpillar_emerge,
/datum/action/cooldown/mob_cooldown/spin_mothsilk = BB_NIAN_CATERPILLAR_SPIN_MOTHSILK_ACTION,
)
/// List of stuff that we want to eat
@@ -177,6 +178,28 @@
user.evolve(C, H)
addtimer(CALLBACK(src, PROC_REF(emerge), C), COCOON_EMERGE_DELAY, TIMER_UNIQUE)
/// Place some grappling tentacles underfoot
/datum/action/cooldown/mob_cooldown/spin_mothsilk
name = "Spin Mothsilk"
desc = "Spin some mothsilk, consuming nutrition."
button_icon = 'icons/obj/stacks/organic.dmi'
button_icon_state = "stack-mothsilk-2"
cooldown_time = 20 SECONDS
click_to_activate = FALSE
shared_cooldown = NONE
/datum/action/cooldown/mob_cooldown/spin_mothsilk/Activate(atom/target)
if(owner.nutrition < 425)
to_chat(owner, "<span class='warning'>You don't have enough nutrition to spin silk!</span>")
return FALSE
if(!do_after(owner, 3 SECONDS))
return FALSE
new /obj/item/stack/sheet/mothsilk(get_turf(owner))
owner.nutrition -= 15
owner.visible_message("<span class='notice'>[owner] spins some mothsilk!</span>")
StartCooldown()
return TRUE
/datum/ai_controller/basic_controller/mothroach
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
@@ -190,6 +213,7 @@
planning_subtrees = list(
/datum/ai_planning_subtree/random_speech/moth_caterpillar,
/datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee,
/datum/ai_planning_subtree/spin_mothsilk,
/datum/ai_planning_subtree/flee_target,
/datum/ai_planning_subtree/find_food/nian_caterpillar,
)
@@ -209,3 +233,22 @@
speech_chance = 2
emote_hear = list("flutters.", "chitters.", "chatters.")
emote_see = list("flutters.", "chitters.", "chatters.")
/datum/ai_planning_subtree/spin_mothsilk
/// Key storing the mothsilk spin action
var/action_key = BB_NIAN_CATERPILLAR_SPIN_MOTHSILK_ACTION
/datum/ai_planning_subtree/spin_mothsilk/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
var/mob/living/basic/nian_caterpillar/caterpillar = controller.pawn
var/datum/action/cooldown/mob_cooldown/spin_mothsilk/silk_action = controller.blackboard[action_key]
if(caterpillar.nutrition >= 425 && silk_action.IsAvailable())
controller.queue_behavior(/datum/ai_behavior/spin_mothsilk, action_key)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/ai_behavior/spin_mothsilk/perform(seconds_per_tick, datum/ai_controller/controller, action_key)
. = ..()
var/datum/action/cooldown/mob_cooldown/spin_mothsilk/silk_action = controller.blackboard[action_key]
var/result = silk_action.Trigger()
if(result)
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
@@ -0,0 +1,49 @@
/obj/item/smithed_item/component/knife_blade
name = "Debug knife blade"
desc = ABSTRACT_TYPE_DESC
icon_state = "knife_blade"
part_type = PART_PRIMARY
hammer_time = 4
/obj/item/smithed_item/component/knife_blade/utility
name = "utility knife blade"
desc = "Smithed component part of a utility knife. The blade has yet to be honed."
materials = list(MAT_METAL = 18000)
finished_product = /obj/item/kitchen/knife/smithed/utility
/obj/item/smithed_item/component/knife_blade/throwing
name = "throwing knife blade"
desc = "Lightweight smithed component part of a knife. The shape is optimally balanced."
materials = list(MAT_METAL = 9000, MAT_TITANIUM = 9000)
finished_product = /obj/item/kitchen/knife/smithed/thrown
/obj/item/smithed_item/component/knife_blade/combat
name = "combat knife blade"
desc = "Heavyweight smithed component part of a knife. A toothed edge runs across the top."
materials = list(MAT_TITANIUM = 9000, MAT_PLASMA = 9000)
finished_product = /obj/item/kitchen/knife/smithed/combat
/obj/item/smithed_item/component/knife_handle
name = "Debug knife handle"
desc = ABSTRACT_TYPE_DESC
icon_state = "knife_handle"
part_type = PART_SECONDARY
hammer_time = 2
/obj/item/smithed_item/component/knife_handle/utility
name = "utility knife handle"
desc = "Smithed component part of a utility knife. The handle fits comfortably in your hand."
materials = list(MAT_METAL = 6000)
finished_product = /obj/item/kitchen/knife/smithed/utility
/obj/item/smithed_item/component/knife_handle/throwing
name = "throwing knife blade"
desc = "Lightweight smithed component part of a knife. There is a small ring on the end of the handle."
materials = list(MAT_METAL = 3000, MAT_TITANIUM = 3000)
finished_product = /obj/item/kitchen/knife/smithed/thrown
/obj/item/smithed_item/component/knife_handle/combat
name = "combat knife blade"
desc = "Heavyweight smithed component part of a knife. The handle is thick and rugged."
materials = list(MAT_TITANIUM = 3000, MAT_PLASMA = 3000)
finished_product = /obj/item/kitchen/knife/smithed/combat
@@ -16,7 +16,7 @@
/// Trim component
var/obj/item/smithed_item/component/trim
/// Finished product
var/obj/item/smithed_item/finished_product
var/obj/item/finished_product
/// Products that are produced in batches
var/list/batched_item_types = list(
/obj/item/smithed_item/lens,
@@ -230,10 +230,21 @@
for(var/datum/smith_quality/quality in quality_list)
if(quality.stat_mult < lowest.stat_mult)
lowest = quality
finished_product.quality = lowest
finished_product.material = trim.material
finished_product.set_stats()
finished_product.update_appearance(UPDATE_NAME)
if(istype(finished_product, /obj/item/smithed_item))
var/obj/item/smithed_item/product = finished_product
product.quality = lowest
product.material = trim.material
product.set_stats()
product.update_appearance(UPDATE_NAME)
finished_product = product
else if(istype(finished_product, /obj/item/kitchen/knife/smithed)) // We need to separate these because of their different paths
var/obj/item/kitchen/knife/smithed/product = finished_product
product.quality = lowest
product.material = trim.material
product.set_stats()
product.update_appearance(UPDATE_NAME)
product.update_icon(UPDATE_OVERLAYS)
finished_product = product
qdel(primary)
qdel(secondary)
qdel(trim)
@@ -256,12 +267,23 @@
if(is_type_in_typecache(finished_product, batched_item_types))
total_extras += clamp(round(batch_extras * i.bit_productivity_mod / 2), 1, 4)
for(var/iterator in 1 to total_extras)
var/obj/item/smithed_item/extra_product = new finished_product.type(src.loc)
extra_product.quality = finished_product.quality
extra_product.material = finished_product.material
extra_product.set_stats()
extra_product.update_appearance(UPDATE_NAME)
extra_product.scatter_atom()
var/obj/item/product
if(istype(finished_product, /obj/item/smithed_item))
var/obj/item/smithed_item/current_product = finished_product
var/obj/item/smithed_item/extra_product = new finished_product.type(src.loc)
extra_product.quality = current_product.quality
extra_product.material = current_product.material
extra_product.set_stats()
product = extra_product
else if(istype(finished_product, /obj/item/kitchen/knife/smithed)) // We need to separate these because of their different paths
var/obj/item/kitchen/knife/smithed/current_product = finished_product
var/obj/item/kitchen/knife/smithed/extra_product = new finished_product.type(src.loc)
extra_product.quality = current_product.quality
extra_product.material = current_product.material
extra_product.set_stats()
product = extra_product
product.update_appearance(UPDATE_NAME)
product.scatter_atom()
finished_product = null
+110
View File
@@ -84,6 +84,12 @@
var/fire_rate_multiplier = 1.0
/// Multiplier for tool uses and lens uses.
var/durability_mult = 1.0
/// Force multiplier
var/force_mult = 1.0
/// Throw force multiplier
var/throw_force_mult = 1.0
/// Throw embed chance multiplier
var/embed_chance_mult = 1.0
/// Is this material valid for secondary goals?
var/secondary_goal_candidate = FALSE
/// How much is this secondary goal worth?
@@ -103,6 +109,9 @@
durability_mult = MINOR_MATERIAL_BUFF
secondary_goal_candidate = TRUE
secondary_goal_difficulty = SMITH_GOAL_EASY
force_mult = MINOR_MATERIAL_DEBUFF
throw_force_mult = MINOR_MATERIAL_DEBUFF
embed_chance_mult = MINOR_MATERIAL_DEBUFF
color_tint = "#78787b"
/datum/smith_material/silver
@@ -116,6 +125,9 @@
projectile_damage_multiplier = MINOR_MATERIAL_DEBUFF
secondary_goal_candidate = TRUE
secondary_goal_difficulty = SMITH_GOAL_MEDIUM
force_mult = MINOR_MATERIAL_DEBUFF
throw_force_mult = MINOR_MATERIAL_BUFF
embed_chance_mult = MINOR_MATERIAL_DEBUFF
color_tint = "#f0f0f0"
/datum/smith_material/gold
@@ -133,6 +145,9 @@
durability_mult = MINOR_MATERIAL_DEBUFF
secondary_goal_candidate = TRUE
secondary_goal_difficulty = SMITH_GOAL_MEDIUM
force_mult = MINOR_MATERIAL_DEBUFF
throw_force_mult = MINOR_MATERIAL_BUFF
embed_chance_mult = MINOR_MATERIAL_DEBUFF
color_tint = "#ffd659"
/datum/smith_material/plasma
@@ -146,6 +161,9 @@
durability_mult = MINOR_MATERIAL_DEBUFF
secondary_goal_candidate = TRUE
secondary_goal_difficulty = SMITH_GOAL_EASY
force_mult = MINOR_MATERIAL_BUFF
throw_force_mult = MINOR_MATERIAL_BUFF
embed_chance_mult = MAJOR_MATERIAL_DEBUFF
color_tint = "#ba3692"
/datum/smith_material/titanium
@@ -161,6 +179,9 @@
fire_rate_multiplier = MINOR_MATERIAL_DEBUFF
secondary_goal_candidate = TRUE
secondary_goal_difficulty = SMITH_GOAL_EASY
force_mult = MINOR_MATERIAL_BUFF
throw_force_mult = MINOR_MATERIAL_DEBUFF
embed_chance_mult = MINOR_MATERIAL_BUFF
color_tint = "#c1c9cf"
/datum/smith_material/uranium
@@ -182,6 +203,9 @@
secondary_goal_candidate = TRUE
secondary_goal_difficulty = SMITH_GOAL_MEDIUM
color_tint = "#2c8c46"
force_mult = MAJOR_MATERIAL_BUFF
throw_force_mult = MAJOR_MATERIAL_BUFF
embed_chance_mult = MAJOR_MATERIAL_DEBUFF
/// Radioactive, woah
var/radiation_amount = 15
@@ -194,6 +218,9 @@
tool_failure_mult = MINOR_MATERIAL_DEBUFF
tool_productivity_mult = MAJOR_MATERIAL_BUFF
durability_mult = MAJOR_MATERIAL_BUFF
force_mult = MAJOR_MATERIAL_BUFF
throw_force_mult = MINOR_MATERIAL_BUFF
embed_chance_mult = MAJOR_MATERIAL_DEBUFF
color_tint = "#aef2f4"
/datum/smith_material/bluespace
@@ -208,6 +235,9 @@
tool_productivity_mult = MAJOR_MATERIAL_BUFF
power_draw_mult = MAJOR_MATERIAL_BUFF
projectile_damage_multiplier = MAJOR_MATERIAL_BUFF
force_mult = MAJOR_MATERIAL_DEBUFF
throw_force_mult = MAJOR_MATERIAL_BUFF
embed_chance_mult = MAJOR_MATERIAL_BUFF
color_tint = "#2e50b7"
/datum/smith_material/plasteel
@@ -226,6 +256,9 @@
durability_mult = MAJOR_MATERIAL_BUFF
secondary_goal_candidate = TRUE
secondary_goal_difficulty = SMITH_GOAL_MEDIUM
force_mult = MAJOR_MATERIAL_BUFF
throw_force_mult = MAJOR_MATERIAL_DEBUFF
embed_chance_mult = MINOR_MATERIAL_DEBUFF
color_tint = "#555053"
/datum/smith_material/plastitanium
@@ -245,6 +278,9 @@
durability_mult = MAJOR_MATERIAL_BUFF
secondary_goal_candidate = TRUE
secondary_goal_difficulty = SMITH_GOAL_HARD
force_mult = MAJOR_MATERIAL_BUFF
throw_force_mult = MAJOR_MATERIAL_DEBUFF
embed_chance_mult = MAJOR_MATERIAL_DEBUFF
color_tint = "#8a838a"
/datum/smith_material/iridium
@@ -262,6 +298,9 @@
power_draw_mult = MINOR_MATERIAL_BUFF
projectile_damage_multiplier = MINOR_MATERIAL_BUFF
durability_mult = MINOR_MATERIAL_BUFF
force_mult = MINOR_MATERIAL_BUFF
throw_force_mult = MINOR_MATERIAL_BUFF
embed_chance_mult = MINOR_MATERIAL_BUFF
color_tint = "#62c3cc"
/datum/smith_material/palladium
@@ -279,6 +318,9 @@
power_draw_mult = MINOR_MATERIAL_DEBUFF
projectile_damage_multiplier = MINOR_MATERIAL_DEBUFF
durability_mult = MINOR_MATERIAL_BUFF
force_mult = MINOR_MATERIAL_BUFF
throw_force_mult = MAJOR_MATERIAL_BUFF
embed_chance_mult = MAJOR_MATERIAL_BUFF
color_tint = "#d36717"
/datum/smith_material/platinum
@@ -296,6 +338,9 @@
projectile_damage_multiplier = MINOR_MATERIAL_BUFF
power_draw_mult = MINOR_MATERIAL_BUFF
durability_mult = MINOR_MATERIAL_BUFF
force_mult = MAJOR_MATERIAL_BUFF
throw_force_mult = MINOR_MATERIAL_BUFF
embed_chance_mult = MAJOR_MATERIAL_BUFF
color_tint = "#c7d3f9"
/datum/smith_material/brass
@@ -316,9 +361,74 @@
durability_mult = MINOR_MATERIAL_DEBUFF
secondary_goal_candidate = TRUE
secondary_goal_difficulty = SMITH_GOAL_MEDIUM
force_mult = MAJOR_MATERIAL_DEBUFF
throw_force_mult = MAJOR_MATERIAL_DEBUFF
embed_chance_mult = MAJOR_MATERIAL_BUFF
color_tint = "#97681b"
#undef MAJOR_MATERIAL_BUFF
#undef MINOR_MATERIAL_BUFF
#undef MINOR_MATERIAL_DEBUFF
#undef MAJOR_MATERIAL_DEBUFF
// Wrappings
/datum/handle_wrapping
var/name = "Boring Debug"
/// Wrap overlay icon
var/wrap_overlay = "basic_wrap"
/// Speed modifier
var/speed_mod = 0.0
/// Productivity mod
var/productivity_mod = 0
/// damage increase
var/force_increase = 0
/// throw damage increase
var/throw_force_increase = 0
/// throw embed chance increase
var/embed_chance_increase = 0
/// Is the wrapping conductive
var/conductive = FALSE
/datum/handle_wrapping/cable
name = "cable wrapping"
wrap_overlay = "cable_wrap"
speed_mod = 0.1
productivity_mod = -0.1
force_increase = 1
conductive = TRUE
/datum/handle_wrapping/cloth
name = "cloth wrapping"
wrap_overlay = "cloth_wrap"
speed_mod = 0.1
productivity_mod = 0.1
/datum/handle_wrapping/leather
name = "leather wrapping"
wrap_overlay = "leather_wrap"
force_increase = 1
throw_force_increase = 1
embed_chance_increase = 5
/datum/handle_wrapping/durathread
name = "durathread wrapping"
wrap_overlay = "durathread_wrap"
speed_mod = 0.15
productivity_mod = 0.15
/datum/handle_wrapping/goliath_hide
name = "goliath hide wrapping"
wrap_overlay = "goliath_wrap"
force_increase = 1
throw_force_increase = 1
embed_chance_increase = 10
/datum/handle_wrapping/mothsilk
name = "mothsilk wrapping"
wrap_overlay = "moth_wrap"
speed_mod = 0.2
productivity_mod = 0.2
force_increase = 1.5
throw_force_increase = 1.5
embed_chance_increase = 10
@@ -0,0 +1,151 @@
/obj/item/kitchen/knife/smithed
name = "debug smithed knife"
desc = ABSTRACT_TYPE_DESC
icon = 'icons/obj/smithing.dmi'
icon_state = "debug"
slot_flags = ITEM_SLOT_BELT
new_attack_chain = TRUE
/// The quality of the item
var/datum/smith_quality/quality
/// The material of the item
var/datum/smith_material/material
/// Base Speed modifier
var/base_speed_mod = 0
/// Speed modifier
var/speed_mod = 1.0
/// Base productivity modifier
var/base_productivity_mod = 0
/// Productivity mod
var/productivity_mod = 1.0
/// damage increase
var/force_increase = 0
/// throw damage increase
var/throw_force_increase = 0
/// throw embed chance increase
var/embed_chance_increase = 0
/// Handle wrappings
var/datum/handle_wrapping/wrap_type
/obj/item/kitchen/knife/smithed/Initialize(mapload)
. = ..()
set_name()
/obj/item/kitchen/knife/smithed/proc/set_name()
if(!quality)
return
if(!material)
name = "[quality.name] " + name
return
name = "[quality.name] [material.name] [initial(name)]"
/obj/item/kitchen/knife/smithed/proc/set_stats()
speed_mod = 1 + (base_speed_mod * quality.stat_mult * material.tool_speed_mult)
productivity_mod = 1 + (base_productivity_mod * quality.stat_mult * material.tool_productivity_mult)
force_increase = initial(force_increase) * quality.stat_mult * material.force_mult
throw_force_increase = initial(throw_force_increase) * quality.stat_mult * material.throw_force_mult
embed_chance_increase = initial(embed_chance_increase) * quality.stat_mult * material.embed_chance_mult
// Now we adjust the item's actual stats
force = initial(force) + force_increase
throwforce = initial(throwforce) + throw_force_increase
embed_chance = initial(embed_chance) + embed_chance_increase
// We adjust bit productivity and similar stats here because they are one and the same
bit_productivity_mod = initial(bit_productivity_mod) * productivity_mod
toolspeed = initial(toolspeed) * speed_mod
// Color
color = material.color_tint
// Radioactive baby
if((material == /datum/smith_material/uranium || istype(material, /datum/smith_material/uranium)) && quality)
var/datum/component/inherent_radioactivity/radioactivity = AddComponent(/datum/component/inherent_radioactivity, 100 * quality.stat_mult, 0, 0, 1.5)
START_PROCESSING(SSradiation, radioactivity)
/obj/item/kitchen/knife/smithed/update_overlays()
. = ..()
overlays.Cut()
if(!wrap_type)
. += "basic_wrap"
return
. += wrap_type.wrap_overlay
/obj/item/kitchen/knife/smithed/update_name()
. = ..()
set_name()
/obj/item/kitchen/knife/smithed/proc/attach_wrapping(datum/handle_wrapping/wrap)
force += wrap.force_increase
throwforce += wrap.throw_force_increase
toolspeed += wrap.speed_mod
bit_productivity_mod += wrap.productivity_mod
embed_chance += wrap.embed_chance_increase
wrap_type = wrap
update_icon(UPDATE_OVERLAYS)
/obj/item/kitchen/knife/smithed/proc/remove_wrapping()
force -= wrap_type.force_increase
throwforce -= wrap_type.throw_force_increase
toolspeed -= wrap_type.speed_mod
bit_productivity_mod -= wrap_type.productivity_mod
embed_chance -= wrap_type.embed_chance_increase
wrap_type = null
update_icon(UPDATE_OVERLAYS)
/obj/item/kitchen/knife/smithed/item_interaction(mob/living/user, obj/item/used, list/modifiers)
. = ..()
if(!isstack(used))
return ITEM_INTERACT_COMPLETE
var/obj/item/stack/stacked_item = used
if(wrap_type)
to_chat(user, "<span class='warning'>There is already a wrap on [src]!</span>")
return ITEM_INTERACT_COMPLETE
var/wrap_to_attach
if(istype(stacked_item, /obj/item/stack/cable_coil))
wrap_to_attach = /datum/handle_wrapping/cable
if(istype(stacked_item, /obj/item/stack/sheet/cloth))
wrap_to_attach = /datum/handle_wrapping/cloth
if(istype(stacked_item, /obj/item/stack/sheet/durathread))
wrap_to_attach = /datum/handle_wrapping/durathread
if(istype(stacked_item, /obj/item/stack/sheet/leather))
wrap_to_attach = /datum/handle_wrapping/leather
if(istype(stacked_item, /obj/item/stack/sheet/animalhide/goliath_hide))
wrap_to_attach = /datum/handle_wrapping/goliath_hide
if(istype(stacked_item, /obj/item/stack/sheet/mothsilk))
wrap_to_attach = /datum/handle_wrapping/mothsilk
if(!wrap_to_attach)
to_chat(user, "<span class='warning'>You cannot wrap [stacked_item] around [src]!</span>")
return ITEM_INTERACT_COMPLETE
if(do_after_once(user, 5 SECONDS, target = src))
if(stacked_item.use(5))
attach_wrapping(wrap_to_attach)
return ITEM_INTERACT_COMPLETE
/obj/item/kitchen/knife/smithed/wirecutter_act(mob/living/user, obj/item/I)
. = ..()
if(wrap_type)
to_chat(user, "<span class='notice'>You cut off the wrap on [src].</span>")
remove_wrapping()
/obj/item/kitchen/knife/smithed/utility
name = "utility knife"
desc = "A custom-made knife designed for general purpose use."
icon_state = "utility_knife"
base_speed_mod = 0.25
base_productivity_mod = 0.25
/obj/item/kitchen/knife/smithed/thrown
name = "throwing knife"
desc = "A lightweight, balanced throwing knife. The sharp blade enhances the chance to embed."
icon_state = "throwing_knife"
base_speed_mod = -0.25
base_productivity_mod = -0.25
throw_force_increase = 6 // 16 throw force at standard, 22 throw force at masterwork
embed_chance_increase = 25 // +25% chance to embed at standard, +50% at masterwork
/obj/item/kitchen/knife/smithed/combat
name = "combat knife"
desc = "A heavyweight, toothed-edge combat knife. The thick blade is designed to rip and tear, and it has an integrated bayonet mount."
icon_state = "combat_knife"
base_productivity_mod = -0.25
force_increase = 6 // 16 force at standard, 22 force at masterwork
embed_chance_increase = 5
bayonet = TRUE
+14
View File
@@ -166,6 +166,20 @@
product_type = /obj/item/smithed_item/component/lens_focus
basin_state = "cast_focus"
/obj/item/smithing_cast/component/knife_blade
name = "knife blade cast"
desc = "A cast for creating knife blades."
icon_state = "knife_blade_cast"
product_type = /obj/item/smithed_item/component/knife_blade
basin_state = "cast_knife_blade"
/obj/item/smithing_cast/component/knife_handle
name = "knife handle cast"
desc = "A cast for creating knife handles."
icon_state = "knife_handle_cast"
product_type = /obj/item/smithed_item/component/knife_handle
basin_state = "cast_knife_handle"
/obj/item/smithing_cast/component/trim
name = "trim cast"
icon_state = "trim_cast"