Skip to content

Commit

Permalink
Added the Combat Bakery Kit (ParadiseSS13#21901)
Browse files Browse the repository at this point in the history
* QUASO

* Apply suggestions from code review

Co-authored-by: Luc <[email protected]>

* blame lewc for giving me spaces

* Update code/datums/uplink_items/uplink_traitor.dm

Co-authored-by: Burzah <[email protected]>

* Update code/datums/components/boomerang.dm

Co-authored-by: Luc <[email protected]>

* Apply suggestions from code review

Co-authored-by: Henri215 <[email protected]>

* Update code/game/objects/items/granters/_granters.dm

Co-authored-by: Luc <[email protected]>

* purge that

---------

Co-authored-by: Luc <[email protected]>
Co-authored-by: Burzah <[email protected]>
Co-authored-by: Henri215 <[email protected]>
  • Loading branch information
4 people committed Sep 13, 2023
1 parent 0e371e1 commit d42d2f6
Show file tree
Hide file tree
Showing 14 changed files with 289 additions and 0 deletions.
2 changes: 2 additions & 0 deletions code/__DEFINES/dcs/signals.dm
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,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
1 change: 1 addition & 0 deletions code/controllers/subsystem/SSthrowing.dm
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ SUBSYSTEM_DEF(throwing)

if(callback)
callback.Invoke()
SEND_SIGNAL(thrownthing, COMSIG_MOVABLE_THROW_LANDED, src)
thrownthing.end_throw()

/datum/thrownthing/proc/hit_atom(atom/A)
Expand Down
86 changes: 86 additions & 0 deletions code/datums/components/boomerang.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
///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
true_parent.visible_message("<span class='danger'>[true_parent] is flying back at [throwing_datum.thrower]!</span>", \
"<span class='danger'>You see [true_parent] fly back at you!</span>", \
"<span class='hear'>You hear an aerodynamic woosh!</span>")

#undef BOOMERANG_REBOUND_INTERVAL
10 changes: 10 additions & 0 deletions code/datums/spells/charge.dm
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@
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 Down
9 changes: 9 additions & 0 deletions code/datums/uplink_items/uplink_traitor.dm
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@
cost = 50
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."
reference = "CBK"
item = /obj/item/storage/box/syndie_kit/combat_baking
cost = 25 //A chef can get a knife that sharp easily, though it won't block. While you can get endless boomerang, they are less deadly than a stech, and slower / more predictable.
job = list("Mime", "Chef")

/datum/uplink_item/jobspecific/pressure_mod
name = "Kinetic Accelerator Pressure Mod"
desc = "A modification kit which allows Kinetic Accelerators to do greatly increased damage while indoors. Occupies 35% mod capacity."
Expand Down
106 changes: 106 additions & 0 deletions code/game/objects/items/granters/_granters.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* 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 class='warning'>You're already reading this!</span>")
return FALSE
if(!user.has_vision())
to_chat(user, "<span class='warning'>You are blind and can't read anything!</span>")
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 class='notice'>You start reading [name]...</span>")

/// Called when the reading is interrupted without finishing.
/obj/item/book/granter/proc/on_reading_stopped(mob/living/user)
to_chat(user, "<span class='notice'>You stop reading...</span>")

/// 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 class='notice'>You finish reading [name]!</span>")

/// 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 class='notice'>[length(remarks) ? pick(remarks) : "You keep reading..."]</span>")
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 class='warning'>You already know all about [action_name]!</span>")
return FALSE
return TRUE

/obj/item/book/granter/action/on_reading_start(mob/living/user)
to_chat(user, "<span class='notice'>You start reading about [action_name]...</span>")

/obj/item/book/granter/action/on_reading_finished(mob/living/user)
to_chat(user, "<span class='notice'>You feel like you've got a good handle on [action_name]!</span>")
// 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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/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)
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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/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)
5 changes: 5 additions & 0 deletions code/game/objects/items/weapons/storage/uplink_kits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,11 @@
new /obj/item/spellbook/oneuse/mime/greaterwall(src)
new /obj/item/spellbook/oneuse/mime/fingergun(src)

/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)
new /obj/item/book/granter/crafting_recipe/combat_baking(src)

/obj/item/storage/box/syndie_kit/atmosn2ogrenades
name = "atmos N2O grenades"
Expand Down
11 changes: 11 additions & 0 deletions code/modules/crafting/recipes.dm
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON

/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)
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
always_availible = FALSE

/datum/crafting_recipe/advancedegun
name = "Advanced Energy Gun"
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
Expand Down
10 changes: 10 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 @@ -551,5 +551,15 @@
list_reagents = list("nutriment" = 4, "sugar" = 2)
tastes = list("croissant" = 1)

/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)

#undef DONUT_NORMAL
#undef DONUT_FROSTED
8 changes: 8 additions & 0 deletions code/modules/food_and_drinks/food/foods/bread.dm
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,14 @@
list_reagents = list("nutriment" = 6, "vitamin" = 1)
tastes = list("bread" = 2)

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

/obj/item/reagent_containers/food/snacks/baguette/combat/Initialize(mapload)
. = ..()
AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.5, _parryable_attack_types = ALL_ATTACK_TYPES)

/obj/item/reagent_containers/food/snacks/twobread
name = "two bread"
desc = "It is very bitter and winy."
Expand Down
Binary file modified icons/obj/library.dmi
Binary file not shown.
4 changes: 4 additions & 0 deletions paradise.dme
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@
#include "code\datums\cache\crew.dm"
#include "code\datums\cache\powermonitor.dm"
#include "code\datums\components\_component.dm"
#include "code\datums\components\boomerang.dm"
#include "code\datums\components\caltrop.dm"
#include "code\datums\components\deadchat_control.dm"
#include "code\datums\components\decal.dm"
Expand Down Expand Up @@ -987,6 +988,9 @@
#include "code\game\objects\items\devices\radio\headset.dm"
#include "code\game\objects\items\devices\radio\intercom.dm"
#include "code\game\objects\items\devices\radio\radio_objects.dm"
#include "code\game\objects\items\granters\_granters.dm"
#include "code\game\objects\items\granters\crafting_granters\_crafting_granter.dm"
#include "code\game\objects\items\granters\crafting_granters\combat_baking.dm"
#include "code\game\objects\items\mountable_frames\air_alarm_frame.dm"
#include "code\game\objects\items\mountable_frames\apc_frame.dm"
#include "code\game\objects\items\mountable_frames\buttons_switches.dm"
Expand Down

0 comments on commit d42d2f6

Please sign in to comment.