Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: Combat Bakery Kit #4576

Merged
merged 7 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions code/__DEFINES/dcs/signals.dm
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,8 @@
#define COMPONENT_CANCEL_THROW (1<<0)
///from base of atom/movable/throw_at(): (datum/thrownthing, spin)
#define COMSIG_MOVABLE_POST_THROW "movable_post_throw"
///from base of datum/thrownthing/finalize(): (obj/thrown_object, datum/thrownthing) used for when a throw is finished
#define COMSIG_MOVABLE_THROW_LANDED "movable_throw_landed"
///from base of atom/movable/onTransitZ(): (old_z, new_z)
#define COMSIG_MOVABLE_Z_CHANGED "movable_ztransit"
///called when the movable is placed in an unaccessible area, used for stationloving: ()
Expand Down
2 changes: 1 addition & 1 deletion code/controllers/subsystem/throwing.dm
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ SUBSYSTEM_DEF(throwing)
thrownthing.newtonian_move(GetOppositeDir(init_dir))

callback?.Invoke()

SEND_SIGNAL(thrownthing, COMSIG_MOVABLE_THROW_LANDED, src)
thrownthing?.end_throw()


Expand Down
91 changes: 91 additions & 0 deletions code/datums/components/boomerang.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
///The cooldown period between last_boomerang_throw and it's methods of implementing a rebound proc.
#define BOOMERANG_REBOUND_INTERVAL (1 SECONDS)

/**
* If an object is given the boomerang component, it should be thrown back to the thrower after either hitting it's target, or landing on the thrown tile.
* Thrown objects should be thrown back to the original thrower with this component, a number of tiles defined by boomerang_throw_range.
*/

/datum/component/boomerang
///How far should the boomerang try to travel to return to the thrower?
var/boomerang_throw_range = 3
///If this boomerang is thrown, does it re-enable the throwers throw mode?
var/thrower_easy_catch_enabled = FALSE
///This cooldown prevents our 2 throwing signals from firing too often based on how we implement those signals within thrown impacts.
var/last_boomerang_throw = 0

/datum/component/boomerang/Initialize(boomerang_throw_range, thrower_easy_catch_enabled)
. = ..()
if(!isitem(parent)) //Only items support being thrown around like a boomerang, feel free to make this apply to humans later on.
return COMPONENT_INCOMPATIBLE

//Assignments
src.boomerang_throw_range = boomerang_throw_range
src.thrower_easy_catch_enabled = thrower_easy_catch_enabled

/datum/component/boomerang/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOVABLE_POST_THROW, PROC_REF(prepare_throw)) //Collect data on current thrower and the throwing datum
RegisterSignal(parent, COMSIG_MOVABLE_THROW_LANDED, PROC_REF(return_missed_throw))
RegisterSignal(parent, COMSIG_MOVABLE_IMPACT, PROC_REF(return_hit_throw))

/datum/component/boomerang/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_MOVABLE_POST_THROW, COMSIG_MOVABLE_THROW_LANDED, COMSIG_MOVABLE_IMPACT))

/**
* Proc'd before the first thrown is performed in order to gather information regarding each throw as well as handle throw_mode as necessary.
* * source: Datum src from original signal call.
* * thrown_thing: The thrownthing datum from the parent object's latest throw. Updates thrown_boomerang.
* * spin: Carry over from POST_THROW, the speed of rotation on the boomerang when thrown.
*/

/datum/component/boomerang/proc/prepare_throw(datum/source, datum/thrownthing/thrown_thing, spin)
SIGNAL_HANDLER
if(thrower_easy_catch_enabled && iscarbon(thrown_thing?.thrower))
var/mob/living/carbon/C = thrown_thing.thrower
C.throw_mode_on()

/**
* Proc that triggers when the thrown boomerang hits an object.
* * source: Datum src from original signal call.
* * hit_atom: The atom that has been hit by the boomerang component.
* * init_throwing_datum: The thrownthing datum that originally impacted the object, that we use to build the new throwing datum for the rebound.
*/

/datum/component/boomerang/proc/return_hit_throw(datum/source, atom/hit_atom, datum/thrownthing/init_throwing_datum)
SIGNAL_HANDLER
if(world.time <= last_boomerang_throw)
return
var/obj/item/true_parent = parent
aerodynamic_swing(init_throwing_datum, true_parent)

/**
* Proc that triggers when the thrown boomerang does not hit a target.
* * source: Datum src from original signal call.
* * throwing_datum: The thrownthing datum that originally impacted the object, that we use to build the new throwing datum for the rebound.
*/

/datum/component/boomerang/proc/return_missed_throw(datum/source, datum/thrownthing/throwing_datum)
SIGNAL_HANDLER
if(world.time <= last_boomerang_throw)
return
var/obj/item/true_parent = parent
aerodynamic_swing(throwing_datum, true_parent)

/**
* Proc that triggers when the thrown boomerang has been fully thrown, rethrowing the boomerang back to the thrower, and producing visible feedback.
* * throwing_datum: The thrownthing datum that originally impacted the object, that we use to build the new throwing datum for the rebound.
* * hit_atom: The atom that has been hit by the boomerang'd object.
*/

/datum/component/boomerang/proc/aerodynamic_swing(datum/thrownthing/throwing_datum, obj/item/true_parent)
var/mob/thrown_by = locateUID(true_parent.thrownby)
if(istype(thrown_by))
var/dir = get_dir(true_parent, thrown_by)
var/turf/T = get_ranged_target_turf(thrown_by, dir, 2)
addtimer(CALLBACK(true_parent, TYPE_PROC_REF(/atom/movable, throw_at), T, boomerang_throw_range, throwing_datum.speed, null, TRUE), 1)
last_boomerang_throw = world.time + BOOMERANG_REBOUND_INTERVAL
thrown_by.visible_message(span_danger("[true_parent] is flying back at [throwing_datum.thrower]!"), \
span_danger("You see [true_parent] fly back at you!"), \
span_hear("You hear an aerodynamic woosh!"))

#undef BOOMERANG_REBOUND_INTERVAL
13 changes: 13 additions & 0 deletions code/datums/spells/charge.dm
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@
to_chat(L, "<span class='caution'>Glowing red letters appear on the front cover...</span>")
to_chat(L, "<span class='warning'>[pick("NICE TRY BUT NO!","CLEVER BUT NOT CLEVER ENOUGH!", "SUCH FLAGRANT CHEESING IS WHY WE ACCEPTED YOUR APPLICATION!", "CUTE!", "YOU DIDN'T THINK IT'D BE THAT EASY, DID YOU?")]</span>")
burnt_out = TRUE

else if(istype(item, /obj/item/book/granter))
var/obj/item/book/granter/I = item
if(prob(80))
L.visible_message("<span class='warning'>[I] catches fire!</span>")
qdel(I)
else
I.uses += 1
charged_item = I
break

else if(istype(item, /obj/item/gun/magic))
var/obj/item/gun/magic/I = item
if(prob(80) && !I.can_charge)
Expand All @@ -63,6 +74,7 @@
W.icon_state = initial(W.icon_state)
charged_item = I
break

else if(istype(item, /obj/item/stock_parts/cell/))
var/obj/item/stock_parts/cell/C = item
if(!C.self_recharge)
Expand All @@ -74,6 +86,7 @@
C.charge = C.maxcharge
charged_item = C
break

else if(item.contents)
var/obj/I = null
for(I in item.contents)
Expand Down
8 changes: 8 additions & 0 deletions code/datums/uplink_item.dm
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,14 @@
cost = 40
job = list("Mime")

/datum/uplink_item/jobspecific/combat_baking
name = "Combat Bakery Kit"
desc = "A kit of clandestine baked weapons. Contains a baguette which a skilled mime could use as a sword, \
a pair of throwing croissants, and the recipe to make more on demand. Once the job is done, eat the evidence."
item = /obj/item/storage/box/syndie_kit/combat_baking
cost = 25
job = list("Mime", "Chef")

//Miner
/datum/uplink_item/jobspecific/pressure_mod
name = "Kinetic Accelerator Pressure Mod"
Expand Down
107 changes: 107 additions & 0 deletions code/game/objects/items/granters/_granters.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* Books that teach things.
*
* (Intrinsic actions like bar flinging, spells like fireball or smoke, or martial arts)
*/

/obj/item/book/granter
/// Flavor messages displayed to mobs reading the granter
var/list/remarks = list()
/// Controls how long a mob must keep the book in his hand to actually successfully learn
var/pages_to_mastery = 3
/// Sanity, whether it's currently being read
var/reading = FALSE
/// The amount of uses on the granter.
var/uses = 1
/// The time it takes to read the book
var/reading_time = 5 SECONDS
/// The sounds played as the user's reading the book.
var/list/book_sounds = list(
'sound/effects/pageturn1.ogg',
'sound/effects/pageturn2.ogg',
'sound/effects/pageturn3.ogg'
)

/obj/item/book/granter/attack_self(mob/living/user)
if(reading)
to_chat(user, span_warning("You're already reading this!"))
return FALSE
if(!user.has_vision())
to_chat(user, span_warning("You are blind and can't read anything!"))
return FALSE
if(!isliving(user))
return FALSE
if(!can_learn(user))
return FALSE

if(uses <= 0)
recoil(user)
return FALSE

on_reading_start(user)
reading = TRUE
for(var/i in 1 to pages_to_mastery)
if(!turn_page(user))
on_reading_stopped()
reading = FALSE
return
if(do_after(user, reading_time, src))
uses--
on_reading_finished(user)
reading = FALSE

return TRUE

/// Called when the user starts to read the granter.
/obj/item/book/granter/proc/on_reading_start(mob/living/user)
to_chat(user, span_notice("You start reading [name]..."))

/// Called when the reading is interrupted without finishing.
/obj/item/book/granter/proc/on_reading_stopped(mob/living/user)
to_chat(user, span_notice("You stop reading..."))

/// Called when the reading is completely finished. This is where the actual granting should happen.
/obj/item/book/granter/proc/on_reading_finished(mob/living/user)
to_chat(user, span_notice("You finish reading [name]!"))

/// The actual "turning over of the page" flavor bit that happens while someone is reading the granter.
/obj/item/book/granter/proc/turn_page(mob/living/user)
playsound(user, pick(book_sounds), 30, TRUE)

if(!do_after(user, reading_time, src))
return FALSE

to_chat(user, span_notice("[length(remarks) ? pick(remarks) : "You keep reading..."]"))
return TRUE

/// Effects that occur whenever the book is read when it has no uses left.
/obj/item/book/granter/proc/recoil(mob/living/user)
return

/// Checks if the user can learn whatever this granter... grants
/obj/item/book/granter/proc/can_learn(mob/living/user)
return TRUE

// Generic action giver
/obj/item/book/granter/action
/// The typepath of action that is given
var/datum/action/granted_action
/// The name of the action, formatted in a more text-friendly way.
var/action_name = ""

/obj/item/book/granter/action/can_learn(mob/living/user)
if(!granted_action)
CRASH("Someone attempted to learn [type], which did not have an action set.")
if(locate(granted_action) in user.actions)
to_chat(user, span_warning("You already know all about [action_name]!"))
return FALSE
return TRUE

/obj/item/book/granter/action/on_reading_start(mob/living/user)
to_chat(user, span_notice("You start reading about [action_name]..."))

/obj/item/book/granter/action/on_reading_finished(mob/living/user)
to_chat(user, span_notice("You feel like you've got a good handle on [action_name]!"))
// Action goes on the mind as the user actually learns the thing in your brain
var/datum/action/new_action = new granted_action(user.mind || user)
new_action.Grant(user)
42 changes: 42 additions & 0 deletions code/game/objects/items/granters/crafting_granters.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/obj/item/book/granter/crafting_recipe
/// A list of all recipe types we grant on learn
var/list/crafting_recipe_types = list()

/obj/item/book/granter/crafting_recipe/on_reading_finished(mob/user)
..()
if(!user.mind)
return
for(var/datum/crafting_recipe/crafting_recipe_type as anything in crafting_recipe_types)
user.mind.teach_crafting_recipe(crafting_recipe_type)
NightDawnFox marked this conversation as resolved.
Show resolved Hide resolved
to_chat(user, "<span class='notice'>You learned how to make [initial(crafting_recipe_type.name)].</span>")

/obj/item/book/granter/crafting_recipe/dusting
icon_state = "book1"

/obj/item/book/granter/crafting_recipe/dusting/recoil(mob/living/user)
to_chat(user, "<span class='notice'>The book turns to dust in your hands.</span>")
qdel(src)
BeebBeebBoob marked this conversation as resolved.
Show resolved Hide resolved

// actual crafting ganters

////Combat baking kit////

/obj/item/book/granter/crafting_recipe/combat_baking
name = "the anarchist's cookbook"
desc = "A widely illegal recipe book which will teach you how to bake croissants to die for."
crafting_recipe_types = list(
/datum/crafting_recipe/throwing_croissant
)
icon_state = "cooking_learing_illegal"
remarks = list(
"\"Austrian? Not French?\"",
"\"Got to get the butter ratio right...\"",
"\"This is the greatest thing since sliced bread!\"",
"\"I'll leave no trace except crumbs!\"",
"\"Who knew that bread could hurt a man so badly?\""
)

/obj/item/book/granter/crafting_recipe/combat_baking/recoil(mob/living/user)
to_chat(user, "<span class='warning'>The book dissolves into burnt flour!</span>")
new /obj/effect/decal/cleanable/ash(get_turf(src))
qdel(src)
10 changes: 10 additions & 0 deletions code/game/objects/items/weapons/storage/uplink_kits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ To apply, hold the injector a short distance away from the outer thigh before ap
/obj/item/storage/box/syndie_kit/knives_kit/populate_contents()
for(var/i in 1 to 7)
new /obj/item/kitchen/knife/combat/throwing(src)

/obj/item/storage/box/syndie_kit/blackops_kit
name = "Black ops kit"

Expand All @@ -539,3 +540,12 @@ To apply, hold the injector a short distance away from the outer thigh before ap
new /obj/item/clothing/accessory/storage/webbing(src)
new /obj/item/storage/belt/military/assault(src)
new /obj/item/clothing/mask/balaclava(src)

/obj/item/storage/box/syndie_kit/combat_baking
name = "Combat Bakery Kit"

/obj/item/storage/box/syndie_kit/combat_baking/populate_contents()
new /obj/item/reagent_containers/food/snacks/baguette/combat(src)
for(var/i in 1 to 2)
new /obj/item/reagent_containers/food/snacks/croissant/throwing(src)
NightDawnFox marked this conversation as resolved.
Show resolved Hide resolved
new /obj/item/book/granter/crafting_recipe/combat_baking(src)
11 changes: 11 additions & 0 deletions code/modules/crafting/recipes.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1424,3 +1424,14 @@
reqs = list(/obj/item/stack/sheet/glass = 3)
tools = list(TOOL_WELDER, TOOL_SCREWDRIVER)
category = CAT_MISC

/datum/crafting_recipe/throwing_croissant
name = "Throwing croissant"
reqs = list(
/obj/item/reagent_containers/food/snacks/croissant = 1,
/obj/item/stack/rods = 1
)
result = list(/obj/item/reagent_containers/food/snacks/croissant/throwing)
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
always_availible = FALSE
BeebBeebBoob marked this conversation as resolved.
Show resolved Hide resolved
20 changes: 20 additions & 0 deletions code/modules/food_and_drinks/food/foods/baked_goods.dm
Original file line number Diff line number Diff line change
Expand Up @@ -833,3 +833,23 @@
tastes = list("banana" = 1, "cherry" = 1, "cream" = 1)
bitesize = 5
foodtype = GRAIN | FRUIT

/obj/item/reagent_containers/food/snacks/croissant
name = "croissant"
desc = "Once a pastry reserved for the bourgeois, this flaky goodness is now on your table."
icon_state = "croissant"
bitesize = 4
filling_color = "#ecb54f"
list_reagents = list("nutriment" = 4, "sugar" = 2)
tastes = list("croissant" = 1)
foodtype = GRAIN

/obj/item/reagent_containers/food/snacks/croissant/throwing
throwforce = 20
throw_range = 9 //now with extra throwing action
tastes = list("croissant" = 2, "butter" = 1, "metal" = 1)
list_reagents = list("nutriment" = 4, "sugar" = 2, "iron" = 1)

/obj/item/reagent_containers/food/snacks/croissant/throwing/Initialize(mapload)
. = ..()
AddComponent(/datum/component/boomerang, throw_range, TRUE)
7 changes: 7 additions & 0 deletions code/modules/food_and_drinks/food/foods/bread.dm
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,18 @@
name = "baguette"
desc = "Bon appetit!"
icon_state = "baguette"
item_state = "baguette"
filling_color = "#E3D796"
bitesize = 3
list_reagents = list("nutriment" = 6, "vitamin" = 1)
tastes = list("bread" = 2)
foodtype = GRAIN
slot_flags = SLOT_BELT

/obj/item/reagent_containers/food/snacks/baguette/combat
sharp = TRUE
force = 20
block_chance = 40

/obj/item/reagent_containers/food/snacks/twobread
name = "two bread"
Expand Down
2 changes: 2 additions & 0 deletions code/modules/food_and_drinks/food/snacks.dm
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
return

/obj/item/reagent_containers/food/snacks/attack(mob/M, mob/user, def_zone)
if(user.a_intent == INTENT_HARM && force)
return ..()
BeebBeebBoob marked this conversation as resolved.
Show resolved Hide resolved
if(!opened)
to_chat(user, "<span class='notice'>You need to open the [src]!</span>")
return
Expand Down
Loading
Loading