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

Its cogging time(again) #4140

Open
wants to merge 42 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b899167
clocksense optimization
wraith-54321 Nov 4, 2024
c8bc820
works
wraith-54321 Nov 7, 2024
b9a24fd
Update anchor_crystal.dm
wraith-54321 Nov 7, 2024
c056153
m
wraith-54321 Nov 10, 2024
777f86c
Update ark_subsystem.dm
wraith-54321 Nov 10, 2024
cfc1921
tee hee
wraith-54321 Nov 11, 2024
8658dab
Update ark_subsystem.dm
wraith-54321 Nov 11, 2024
6688143
Update turf.dm
wraith-54321 Nov 12, 2024
8675bd0
Update clock_cult.dm
wraith-54321 Nov 12, 2024
1d83519
Update ClockworkSlab.jsx
wraith-54321 Nov 12, 2024
55574b6
Update ark_subsystem.dm
wraith-54321 Nov 13, 2024
b13a5de
sghdfrt
wraith-54321 Nov 14, 2024
1b875fc
Update wizard.dm
wraith-54321 Nov 14, 2024
8a3f102
weeeaaagrfg
wraith-54321 Nov 20, 2024
5eb8ad8
Update add_warp_area.dm
wraith-54321 Nov 20, 2024
f4dbeac
Update eminence_beacon.dm
wraith-54321 Nov 22, 2024
e509c26
o
wraith-54321 Nov 25, 2024
cc7a37c
Rewrites anti-magic slightly to stop spamming anti-magic effects on t…
MrMelbert Jul 15, 2023
845b426
multi area convert
wraith-54321 Dec 3, 2024
52e665e
Update _compile_options.dm
wraith-54321 Dec 3, 2024
ac61fb1
things
wraith-54321 Dec 22, 2024
8c70ac8
mmmmm
wraith-54321 Dec 27, 2024
58939f0
Update stargazer.dm
wraith-54321 Dec 27, 2024
505b5e2
Merge remote-tracking branch 'upstream/master' into you-know-the-time
wraith-54321 Dec 27, 2024
7f4aa6d
Update eminence_antag_datum.dm
wraith-54321 Dec 27, 2024
939a689
Update _base.dm
wraith-54321 Dec 27, 2024
2e60323
cool
wraith-54321 Dec 27, 2024
56e1a9b
Merge branch 'master' of https://github.com/Monkestation/Monkestation…
Absolucy Dec 27, 2024
3581bcd
more
wraith-54321 Dec 29, 2024
e7bd052
Merge remote-tracking branch 'upstream/master' into you-know-the-time
wraith-54321 Dec 29, 2024
57d329a
Merge branch 'you-know-the-time' of https://github.com/wraith-54321/M…
wraith-54321 Dec 29, 2024
01a2e60
um
wraith-54321 Dec 29, 2024
055790e
a
wraith-54321 Dec 29, 2024
0cf6987
Update space_fold.dm
wraith-54321 Dec 29, 2024
f31a0a8
Update anchor_crystal.dm
wraith-54321 Dec 29, 2024
61194ad
p
wraith-54321 Dec 29, 2024
546a513
Update reebe_modules.dm
wraith-54321 Dec 30, 2024
138eed8
gooder
wraith-54321 Dec 31, 2024
985a4be
port the antimagic fix
wraith-54321 Jan 19, 2025
bc7fc16
Update abscond.dm
wraith-54321 Jan 20, 2025
6657ee9
Update add_warp_area.dm
wraith-54321 Jan 21, 2025
e2fd0a6
Merge branch 'master' into you-know-the-time
wraith-54321 Jan 21, 2025
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: 1 addition & 1 deletion code/__DEFINES/traits/declarations.dm
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// This allows a person who has antimagic to cast spells without getting blocked
#define TRAIT_ANTIMAGIC_NO_SELFBLOCK "anti_magic_no_selfblock"
/// This mob recently blocked magic with some form of antimagic
/* #define TRAIT_RECENTLY_BLOCKED_MAGIC "recently_blocked_magic" */
#define TRAIT_RECENTLY_BLOCKED_MAGIC "recently_blocked_magic"
/// The user can do things like use magic staffs without penalty
#define TRAIT_MAGICALLY_GIFTED "magically_gifted"
/// This object innately spawns with fantasy variables already applied (the magical component is given to it on initialize), and thus we never want to give it the component again.
Expand Down
2 changes: 1 addition & 1 deletion code/__DEFINES/~monkestation/antagonists.dm
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
// Clock cultist
#define IS_CLOCK(mob) ((FACTION_CLOCK in mob.faction) || mob?.mind?.has_antag_datum(/datum/antagonist/clock_cultist))
/// maximum amount of cogscarabs the clock cult can have
#define MAXIMUM_COGSCARABS 9
#define MAXIMUM_COGSCARABS 6
/// is something a cogscarab
#define iscogscarab(checked) (istype(checked, /mob/living/basic/drone/cogscarab))
/// is something an eminence
Expand Down
10 changes: 8 additions & 2 deletions code/__DEFINES/~monkestation/clock_cult.dm
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,14 @@
///the map path of the reebe map
#define REEBE_MAP_PATH "_maps/~monkestation/templates/reebe.dmm"

///how long in seconds do anchoring crystals take to charge after being placed, 6 minutes
#define ANCHORING_CRYSTAL_CHARGE_DURATION 360 SECONDS

///how long between uses of the anchoring crystal scripture, also how long the hostile environment lasts if the crystal is not destroyed
#define ANCHORING_CRYSTAL_COOLDOWN 7 MINUTES
#define ANCHORING_CRYSTAL_COOLDOWN ANCHORING_CRYSTAL_CHARGE_DURATION + 1 MINUTE

///up to how many tiles away will the ark stop certain things from breaking turfs
#define ARK_TURF_DESTRUCTION_BLOCK_RANGE 9
#define ARK_TURF_DESTRUCTION_BLOCK_RANGE 10

///how many clockwork airlocks is the cult allowed to create on reebe
#define MAXIMUM_REEBE_AIRLOCKS 50
8 changes: 7 additions & 1 deletion code/__DEFINES/~monkestation/dcs/signals/signals_object.dm
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
//flag to block the qdel that normally happens when a projectile is blocked
///flag to block the qdel that normally happens when a projectile is blocked
#define PROJECTILE_INTERRUPT_BLOCK_QDEL (4<<0)

///sent by the ark SS whenever an anchoring crystal charges (/obj/structure/destructible/clockwork/anchoring_crystal/charged_crystal)
#define COMSIG_ANCHORING_CRYSTAL_CHARGED "anchoring_crystal_charged"

///sent by the ark SS whenever an anchoring crystal is created (/obj/structure/destructible/clockwork/anchoring_crystal/charged_crystal)
#define COMSIG_ANCHORING_CRYSTAL_CREATED "anchoring_crystal_created"
6 changes: 5 additions & 1 deletion code/__DEFINES/~monkestation/maps.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
#define ZTRAIT_FORCED_SAFETY "Forced Safety"

///List of ztraits the reebe Z level has
#define ZTRAITS_REEBE list(ZTRAIT_REEBE = TRUE, ZTRAIT_NOPHASE = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 0.5, ZTRAIT_RESERVED = TRUE)
#define ZTRAITS_REEBE list(ZTRAIT_REEBE = TRUE, \
ZTRAIT_NOPHASE = TRUE, \
ZTRAIT_BOMBCAP_MULTIPLIER = 0.5, \
ZTRAIT_RESERVED = TRUE, \
ZTRAIT_BASETURF = /turf/open/indestructible/reebe_flooring)

#define is_safe_level(z) SSmapping.level_trait(z, ZTRAIT_FORCED_SAFETY)
96 changes: 96 additions & 0 deletions code/__HELPERS/~monkestation-helpers/spatial_info.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#define EDGE_STATE_SOUTH_BORDER 1
#define EDGE_STATE_MAP_BOTTOM 2
#define EDGE_STATE_MAP_TOP 3
#define SET_DIR_LIST(dir_list, edge_state) \
switch(edge_state) { \
if(EDGE_STATE_MAP_BOTTOM) { \
##dir_list = GLOB.cardinals - SOUTH;\
} \
if(EDGE_STATE_MAP_TOP) { \
##dir_list = GLOB.cardinals - NORTH;\
} \
else { \
##dir_list = GLOB.cardinals;\
} \
}

#define ADD_NEW_AREAS(dir_list, step_turf, step_of, checked, added_to) \
for(var/dir in dir_list) { \
##step_turf = get_step(step_of, dir);\
if(step_turf && step_turf.loc != checked) { \
##added_to |= step_turf.loc;\
} \
}

///Returns all turfs on the border of an area
/proc/get_area_edge_turfs(area/checked, return_adjacent_areas = FALSE) as /list
RETURN_TYPE(/list)
var/list/returned_list = list()
for(var/i in 1 to length(checked.turfs_by_zlevel))
var/list/z_turfs = checked.turfs_by_zlevel[i]
var/list/new_list = list()
var/last_y = 0
var/edge_state = FALSE
var/turf/last_checked
returned_list.len++
returned_list[i] = new_list
for(var/turf/z_turf in z_turfs)
if(z_turf.y != last_y)
last_y = z_turf.y
if(last_checked)
if(return_adjacent_areas)
var/list/dir_list
SET_DIR_LIST(dir_list, edge_state)
var/turf/step_turf
ADD_NEW_AREAS(dir_list, step_turf, last_checked, checked, new_list)
else
new_list += last_checked

if(z_turf.y == 1)
edge_state = EDGE_STATE_MAP_BOTTOM
else if(last_y == 0)
edge_state = EDGE_STATE_SOUTH_BORDER
else if(z_turf.y == world.maxy)
edge_state = EDGE_STATE_MAP_TOP
else
edge_state = FALSE

if(return_adjacent_areas)
var/list/dir_list
SET_DIR_LIST(dir_list, edge_state)
var/turf/step_turf
ADD_NEW_AREAS(dir_list, step_turf, z_turf, checked, new_list)
else
new_list += z_turf
last_checked = null
continue

if(edge_state)
if(return_adjacent_areas)
var/list/dir_list
SET_DIR_LIST(dir_list, edge_state)
var/turf/step_turf
ADD_NEW_AREAS(dir_list, step_turf, z_turf, checked, new_list)
else
new_list += z_turf
continue

var/turf/north_turf = get_step(z_turf, NORTH)
if(north_turf.loc != checked) //if we dont have a step something else already broke as map borders have already been handled
if(return_adjacent_areas)
var/list/dir_list = GLOB.cardinals - NORTH
if(edge_state == EDGE_STATE_MAP_BOTTOM)
dir_list -= SOUTH
var/turf/step_turf
ADD_NEW_AREAS(dir_list, step_turf, z_turf, checked, new_list)
new_list |= (return_adjacent_areas ? north_turf.loc : z_turf)
last_checked = null
else
last_checked = z_turf
return returned_list

#undef EDGE_STATE_SOUTH_BORDER
#undef EDGE_STATE_MAP_BOTTOM
#undef EDGE_STATE_MAP_TOP
#undef SET_DIR_LIST
#undef ADD_NEW_AREAS
4 changes: 2 additions & 2 deletions code/controllers/subsystem/machines.dm
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ SUBSYSTEM_DEF(machines)
all_machines -= machine

/// Gets a list of all machines that are either the passed type or a subtype.
/datum/controller/subsystem/machines/proc/get_machines_by_type_and_subtypes(obj/machinery/machine_type)
/datum/controller/subsystem/machines/proc/get_machines_by_type_and_subtypes(obj/machinery/machine_type, list/type_exclusions) //monkestation edit: adds type_exclusions
if(!ispath(machine_type))
machine_type = machine_type.type
if(!ispath(machine_type, /obj/machinery))
CRASH("called get_machines_by_type_and_subtypes with a non-machine type [machine_type]")
var/list/machines = list()
for(var/next_type in typesof(machine_type))
for(var/next_type in typesof(machine_type) - type_exclusions) //monkestation edit: adds type_exclusions
var/list/found_machines = machines_by_type[next_type]
if(found_machines)
machines += found_machines
Expand Down
4 changes: 2 additions & 2 deletions code/controllers/subsystem/timer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -610,8 +610,8 @@ SUBSYSTEM_DEF(timer)
* * timer_subsystem the subsystem to insert this timer into
*/
/proc/_addtimer(datum/callback/callback, wait = 0, flags = 0, datum/controller/subsystem/timer/timer_subsystem, file, line)
if (!callback)
CRASH("addtimer called without a callback")
ASSERT(istype(callback), "addtimer called [callback ? "with an invalid callback ([callback])" : "without a callback"]")
ASSERT(isnum(wait), "addtimer called with a non-numeric wait ([wait])")

if (wait < 0)
stack_trace("addtimer called with a negative wait. Converting to [world.tick_lag]")
Expand Down
137 changes: 55 additions & 82 deletions code/datums/components/anti_magic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
/// A bitflag with the types of magic resistance on the object
var/antimagic_flags
/// The amount of times the object can protect the user from magic
/// Set to INFINITY to have, well, infinite charges.
var/charges
/// The inventory slot the object must be located at in order to activate
var/inventory_flags
/// The proc that is triggered when an object has been drained a antimagic charge
/// The callback invoked when we have been drained a antimagic charge
var/datum/callback/drain_antimagic
/// The proc that is triggered when the object is depleted of charges
/// The callback invoked when twe have been depleted of all charges
var/datum/callback/expiration
/// If we have already sent a notification message to the mob picking up an antimagic item
var/casting_restriction_alert = FALSE
/// Whether we should, on equipping, alert the caster that this item can block any of their spells
/// This changes between true and false on equip and drop, don't set it outright to something
var/alert_caster_on_equip = TRUE

/**
* Adds magic resistances to an object
Expand All @@ -36,18 +38,15 @@
charges = INFINITY,
inventory_flags = ~ITEM_SLOT_BACKPACK, // items in a backpack won't activate, anywhere else is fine
datum/callback/drain_antimagic,
datum/callback/expiration
datum/callback/expiration,
)

if(isitem(parent))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
RegisterSignals(parent, list(COMSIG_ITEM_ATTACK, COMSIG_ITEM_ATTACK_OBJ), PROC_REF(on_attack))
else if(ismob(parent))
RegisterSignal(parent, COMSIG_MOB_RECEIVE_MAGIC, PROC_REF(block_receiving_magic), override = TRUE)
RegisterSignal(parent, COMSIG_MOB_RESTRICT_MAGIC, PROC_REF(restrict_casting_magic), override = TRUE)
if(!HAS_TRAIT(parent, TRAIT_ANTIMAGIC_NO_SELFBLOCK))
to_chat(parent, span_warning("Magic seems to flee from you. You are immune to spells but are unable to cast magic."))
register_antimagic_signals(parent)
else
return COMPONENT_INCOMPATIBLE

Expand All @@ -62,92 +61,66 @@
expiration = null
return ..()

/datum/component/anti_magic/proc/on_equip(datum/source, mob/equipper, slot)
/datum/component/anti_magic/proc/register_antimagic_signals(datum/on_what)
RegisterSignal(on_what, COMSIG_MOB_RECEIVE_MAGIC, PROC_REF(block_receiving_magic), override = TRUE)
RegisterSignal(on_what, COMSIG_MOB_RESTRICT_MAGIC, PROC_REF(restrict_casting_magic), override = TRUE)

/datum/component/anti_magic/proc/unregister_antimagic_signals(datum/on_what)
UnregisterSignal(on_what, list(COMSIG_MOB_RECEIVE_MAGIC, COMSIG_MOB_RESTRICT_MAGIC))

/datum/component/anti_magic/proc/on_equip(atom/movable/source, mob/equipper, slot)
SIGNAL_HANDLER

if(!(inventory_flags & slot)) //Check that the slot is valid for antimagic
UnregisterSignal(equipper, COMSIG_MOB_RECEIVE_MAGIC)
UnregisterSignal(equipper, COMSIG_MOB_RESTRICT_MAGIC)
unregister_antimagic_signals(equipper)
return

register_antimagic_signals(equipper)
if(!alert_caster_on_equip)
return
RegisterSignal(equipper, COMSIG_MOB_RECEIVE_MAGIC, PROC_REF(block_receiving_magic), override = TRUE)
RegisterSignal(equipper, COMSIG_MOB_RESTRICT_MAGIC, PROC_REF(restrict_casting_magic), override = TRUE)

if(!casting_restriction_alert)
// Check to see if we have any spells that are blocked due to antimagic
for(var/datum/action/cooldown/spell/magic_spell in equipper.actions)
if(!(magic_spell.spell_requirements & SPELL_REQUIRES_NO_ANTIMAGIC))
continue
// Check to see if we have any spells that are blocked due to antimagic
for(var/datum/action/cooldown/spell/magic_spell in equipper.actions)
if(!(magic_spell.spell_requirements & SPELL_REQUIRES_NO_ANTIMAGIC))
continue

if(!(antimagic_flags & magic_spell.antimagic_flags))
continue

if(antimagic_flags & magic_spell.antimagic_flags)
to_chat(equipper, span_warning("[parent] is interfering with your ability to cast magic!"))
casting_restriction_alert = TRUE
break
to_chat(equipper, span_warning("[parent] is interfering with your ability to cast magic!"))
alert_caster_on_equip = FALSE
break

/datum/component/anti_magic/proc/on_drop(datum/source, mob/user)
/datum/component/anti_magic/proc/on_drop(atom/movable/source, mob/user)
SIGNAL_HANDLER

UnregisterSignal(user, COMSIG_MOB_RECEIVE_MAGIC)
UnregisterSignal(user, COMSIG_MOB_RESTRICT_MAGIC)
casting_restriction_alert = FALSE
// Reset alert
if(source.loc != user)
alert_caster_on_equip = TRUE
unregister_antimagic_signals(user)

/datum/component/anti_magic/proc/block_receiving_magic(mob/living/carbon/user, casted_magic_flags, charge_cost, list/protection_was_used)
/datum/component/anti_magic/proc/block_receiving_magic(mob/living/carbon/source, casted_magic_flags, charge_cost, list/antimagic_sources)
SIGNAL_HANDLER

// if any protection sources exist in our list then we already blocked the magic
if(!istype(user) || protection_was_used.len)
return
// We do not block this type of magic, good day
if(!(casted_magic_flags & antimagic_flags))
return NONE

// disclaimer - All anti_magic sources will be drained a charge_cost
if(casted_magic_flags & antimagic_flags)
var/mutable_appearance/antimagic_effect
var/antimagic_color
// im a programmer not shakesphere to the future grammar nazis that come after me for this
var/visible_subject = ismob(parent) ? "[user.p_they()]" : "[parent]"
var/self_subject = ismob(parent) ? "you" : "[parent]"

if(casted_magic_flags & antimagic_flags & MAGIC_RESISTANCE)
user.visible_message(
span_warning("[user] pulses red as [visible_subject] absorbs magic energy!"),
span_userdanger("An intense magical aura pulses around [self_subject] as it dissipates into the air!"),
)
antimagic_effect = mutable_appearance('icons/effects/effects.dmi', "shield-red", MOB_SHIELD_LAYER)
antimagic_color = LIGHT_COLOR_BLOOD_MAGIC
playsound(user, 'sound/magic/magic_block.ogg', 50, TRUE)
else if(casted_magic_flags & antimagic_flags & MAGIC_RESISTANCE_HOLY)
user.visible_message(
span_warning("[user] starts to glow as [visible_subject] emits a halo of light!"),
span_userdanger("A feeling of warmth washes over [self_subject] as rays of light surround your body and protect you!"),
)
antimagic_effect = mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER)
antimagic_color = LIGHT_COLOR_HOLY_MAGIC
playsound(user, 'sound/magic/magic_block_holy.ogg', 50, TRUE)
else if(casted_magic_flags & antimagic_flags & MAGIC_RESISTANCE_MIND)
user.visible_message(
span_warning("[user] forehead shines as [visible_subject] repulses magic from their mind!"),
span_userdanger("A feeling of cold splashes on [self_subject] as your forehead reflects magic usering your mind!"),
)
antimagic_effect = mutable_appearance('icons/effects/genetics.dmi', "telekinesishead", MOB_SHIELD_LAYER)
antimagic_color = LIGHT_COLOR_DARK_BLUE
playsound(user, 'sound/magic/magic_block_mind.ogg', 50, TRUE)

user.mob_light(range = 2, color = antimagic_color, duration = 5 SECONDS)
user.add_overlay(antimagic_effect)
addtimer(CALLBACK(user, TYPE_PROC_REF(/atom, cut_overlay), antimagic_effect), 50)

if(ismob(parent))
return COMPONENT_MAGIC_BLOCKED

var/has_limited_charges = !(charges == INFINITY)
var/charge_was_drained = charge_cost > 0
if(has_limited_charges && charge_was_drained)
protection_was_used += parent
drain_antimagic?.Invoke(user, parent)
charges -= charge_cost
if(charges <= 0)
expiration?.Invoke(user, parent)
qdel(src)
return COMPONENT_MAGIC_BLOCKED
return NONE
// We have already blocked this spell
if(parent in antimagic_sources)
return NONE

// Block success! Add this parent to the list of antimagic sources
antimagic_sources += parent

if((charges != INFINITY) && charge_cost > 0)
drain_antimagic?.Invoke(source, parent)
charges -= charge_cost
if(charges <= 0)
expiration?.Invoke(source, parent)
qdel(src) // no more antimagic

return COMPONENT_MAGIC_BLOCKED

/// cannot cast magic with the same type of antimagic present
/datum/component/anti_magic/proc/restrict_casting_magic(mob/user, magic_flags)
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/structures/window.dm
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
glass_color = new_color
if(fulltile)
color = glass_color
if(mapload && fulltile)
if(mapload && fulltile && has_sill)
new /obj/structure/window_sill(get_turf(src))
//monkestation edit end

Expand Down
2 changes: 2 additions & 0 deletions code/modules/admin/verbs/adminevents.dm
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@
SSshuttle.admin_emergency_no_recall = TRUE
SSshuttle.emergency.setTimer(0)
SSshuttle.emergency.mode = SHUTTLE_DISABLED
SSshuttle.admin_emergency_disabled = TRUE //monkestation edit
priority_announce(
text = "Emergency Shuttle uplink failure, shuttle disabled until further notice.",
title = "Uplink Failure",
Expand All @@ -249,6 +250,7 @@
message_admins(span_adminnotice("[key_name_admin(usr)] enabled the emergency shuttle."))
SSshuttle.admin_emergency_no_recall = FALSE
SSshuttle.emergency_no_recall = FALSE
SSshuttle.admin_emergency_disabled = FALSE //monkestation edit
if(SSshuttle.last_mode == SHUTTLE_DISABLED) //If everything goes to shit, fix it.
SSshuttle.last_mode = SHUTTLE_IDLE

Expand Down
2 changes: 1 addition & 1 deletion code/modules/antagonists/wizard/wizard.dm
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
if(APPRENTICE_DESTRUCTION)
spells_to_grant = list(
/datum/action/cooldown/spell/aoe/magic_missile,
/datum/action/cooldown/spell/pointed/projectile/fireball/bouncy, //monkestation edit: adds the bouncy subtype
/datum/action/cooldown/spell/pointed/projectile/fireball,
)
to_chat(owner, span_bold("Your service has not gone unrewarded, however. \
Studying under [master.current.real_name], you have learned powerful, \
Expand Down
Loading
Loading