diff --git a/code/controllers/subsystem/game_decorator.dm b/code/controllers/subsystem/game_decorator.dm new file mode 100644 index 000000000000..cfee0406f84a --- /dev/null +++ b/code/controllers/subsystem/game_decorator.dm @@ -0,0 +1,35 @@ +// Essentially the same as decorators but that apply to the whole game state instead of individual atoms +SUBSYSTEM_DEF(game_decorator) + name = "Game Decorator" + init_order = SS_INIT_DECORATOR + flags = SS_NO_FIRE + +/datum/controller/subsystem/game_decorator/Initialize() + . = ..() + for(var/decorator_type in subtypesof(/datum/game_decorator)) + var/datum/game_decorator/decorator = new decorator_type() + if(!decorator.is_active_decor()) + continue + if(!decorator.defer_decoration) + decorator.decorate() + CHECK_TICK + + return SS_INIT_SUCCESS + +/datum/game_decorator + var/defer_decoration = TRUE //! So map decoration is done post-setup after nightmare and spawners + +/datum/game_decorator/New() + if(defer_decoration) + RegisterSignal(SSdcs, COMSIG_GLOB_MODE_POSTSETUP, PROC_REF(defered_decoration)) + +/datum/game_decorator/proc/is_active_decor() + return FALSE + +/datum/game_decorator/proc/defered_decoration(dcs) + SIGNAL_HANDLER + decorate() + +/datum/game_decorator/proc/decorate() + set waitfor = FALSE + return diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm index a4781e1a6609..93eb45c3b79c 100644 --- a/code/game/turfs/open.dm +++ b/code/game/turfs/open.dm @@ -185,6 +185,7 @@ name = "cave" icon = 'icons/turf/floors/bigred.dmi' icon_state = "mars_cave_1" + is_groundmap_turf = TRUE /turf/open/mars_cave/Initialize(mapload, ...) @@ -283,6 +284,7 @@ name = "ground dirt" icon = 'icons/turf/ground_map.dmi' icon_state = "desert" + is_groundmap_turf = TRUE /turf/open/gm/attackby(obj/item/I, mob/user) @@ -646,6 +648,7 @@ baseturfs = /turf/open/gm/riverdeep supports_surgery = FALSE minimap_color = MINIMAP_WATER + is_groundmap_turf = FALSE // Not real ground /turf/open/gm/riverdeep/Initialize(mapload, ...) @@ -724,6 +727,7 @@ allow_construction = FALSE var/bushes_spawn = 1 var/plants_spawn = 1 + is_groundmap_turf = TRUE name = "wet grass" desc = "Thick, long, wet grass." icon = 'icons/turf/floors/jungle.dmi' diff --git a/code/modules/cm_aliens/weeds.dm b/code/modules/cm_aliens/weeds.dm index eb4fc3cd6f09..736d4f947450 100644 --- a/code/modules/cm_aliens/weeds.dm +++ b/code/modules/cm_aliens/weeds.dm @@ -196,7 +196,7 @@ var/parent_type = /obj/effect/alien/weeds/node if(weed_strength >= WEED_LEVEL_HIVE) parent_type = /obj/effect/alien/weeds/node/pylon - + var/obj/effect/alien/weeds/node/found = locate(parent_type) in orange(node_range, get_turf(src)) if(found) found.add_child(src) diff --git a/code/modules/decorators/halloween.dm b/code/modules/decorators/halloween.dm deleted file mode 100644 index af19d80bbb23..000000000000 --- a/code/modules/decorators/halloween.dm +++ /dev/null @@ -1,63 +0,0 @@ -/datum/decorator/halloween - priority = DECORATOR_MONTH_SPECIFIC - -/datum/decorator/halloween/is_active_decor() - return (get_event_progress() != -1) - -/datum/decorator/halloween/proc/get_event_progress() - . = -1 - var/cur_day = text2num(time2text(world.timeofday, "DD")) - var/cur_mon = text2num(time2text(world.timeofday, "MM")) - if(cur_mon == 10) - return min(0, 31 - cur_day) - if(cur_mon == 11 && cur_day < 4) - return 0 - -/// Cobweb decorator: adds more and more cobwebs as you go through the month -/datum/decorator/halloween/cobwebs - /// How much prob() chance to put a cobweb during halloween proper - var/base_chance = 25 - /// How much to remove per day before date - var/ramp_chance = 0.3 - /// How much to scale cobwebs alpha down per day (1 - ramp_scale * days, affects alpha & size) - var/ramp_scale = 0.01 - /// Extra randomness removed onto scale before full blown halloween - var/scale_rand = 0.3 - -/datum/decorator/halloween/cobwebs/decorate(turf/closed/wall/almayer/T) - var/static/list/order = list(NORTHWEST, SOUTHEAST, NORTHEAST, SOUTHWEST) // Ordering of wall_connections - if(length(T.wall_connections) < 4) - return - - var/event_progress = get_event_progress() - var/placement_chance = base_chance - (event_progress * ramp_chance) - for(var/i = 1 to 4) - var/diag = order[i] - if(T.wall_connections[i] != "5") // CORNER_CLOCKWISE | CORNER_COUNTERCLOCKWISE as string - don't ask me - continue - if(!prob(placement_chance)) - continue - - // Skip this if this corner is result of a door connection (mostly for Almayer shutters) - var/valid = TRUE - for(var/a_cardinal in cardinal) - var/cardinal_dir = diag & a_cardinal - if(!a_cardinal) // We check cardinals contributing to that diagonal - continue - var/turf/target = get_step(T, cardinal_dir) - if(locate(/obj/structure/machinery/door) in target) - valid = FALSE - break - - if(valid) // Actually place cobweb - var/turf/target = get_step(T, diag) - if(istype(target, /turf/open)) - var/scale = 1 - ramp_scale * event_progress - scale -= scale_rand * rand() - new /obj/effect/decal/cleanable/cobweb2/dynamic(target, diag, scale) - -/// Ship specific cobweb decorator -/datum/decorator/halloween/cobwebs/ship - -/datum/decorator/halloween/cobwebs/ship/get_decor_types() - return typesof(/turf/closed/wall/almayer) diff --git a/code/modules/holidays/halloween/decorators.dm b/code/modules/holidays/halloween/decorators.dm new file mode 100644 index 000000000000..b25d6ff6b957 --- /dev/null +++ b/code/modules/holidays/halloween/decorators.dm @@ -0,0 +1,108 @@ +/datum/game_decorator/halloween + +/datum/game_decorator/halloween/is_active_decor() + return (get_days_remaining() != -1) + +/// Get the number of days remaining to event, or -1 if not applicable +/datum/game_decorator/halloween/proc/get_days_remaining() + . = -1 + var/cur_day = text2num(time2text(world.timeofday, "DD")) + var/cur_mon = text2num(time2text(world.timeofday, "MM")) + if(cur_mon == 10) + return max(0, 31 - cur_day) + if(cur_mon == 11 && cur_day < 4) + return 0 + +/// Pumpkins decorator: adds patches of carvable/wearable pumpkins around the ground level +/datum/game_decorator/halloween/pumpkins + var/pumpkin_count = 60 //! Amount of pumpkins to place + var/pumpkin_count_decrease = 1 //! Amount of pumpkins to remove per day to halloween + var/pumpkin_prob_corruption = 20 + var/pumpkin_prob_decrease = 0.5 //! Chance reduction per day before halloween + var/exclusion_range = 10 + +/datum/game_decorator/halloween/pumpkins/decorate() + var/list/turf/valid_turfs = list() + var/list/ground_levels = SSmapping.levels_by_trait(ZTRAIT_GROUND) + for(var/ground_z in ground_levels) + var/list/turf/all_turfs = block(locate(1, 1, ground_z), locate(world.maxx, world.maxy, ground_z)) + for(var/turf/open/turf in all_turfs) + if(turf.is_groundmap_turf) + var/valid = TRUE + for(var/atom/movable/movable as anything in turf.contents) + if(movable.density && movable.can_block_movement) + valid = FALSE + break + if(valid) + valid_turfs += turf + CHECK_TICK + + var/list/turf/picked_turfs = list() + for(var/step in 1 to (pumpkin_count - pumpkin_count_decrease * get_days_remaining())) + if(!length(valid_turfs)) + break + var/turf/considered_turf = pick(valid_turfs) + var/x_min = max(1, considered_turf.x - exclusion_range) + var/y_min = max(1, considered_turf.y - exclusion_range) + var/x_max = min(world.maxx, considered_turf.x + exclusion_range) + var/y_max = min(world.maxy, considered_turf.y + exclusion_range) + var/list/turf/denied_turfs = block(locate(x_min, y_min, considered_turf.z), locate(x_max, y_max, considered_turf.z)) + valid_turfs -= denied_turfs + picked_turfs += considered_turf + + var/corruption_chance = pumpkin_prob_corruption - (get_days_remaining() * pumpkin_prob_decrease) + for(var/turf/target in picked_turfs) + if(prob(corruption_chance)) + new /obj/structure/pumpkin_patch/corrupted(target) + else + new /obj/structure/pumpkin_patch(target) + +/// Cobweb decorator: adds more and more cobwebs as you go through the month +/datum/game_decorator/halloween/cobwebs + /// How much prob() chance to put a cobweb during halloween proper + var/base_chance = 25 + /// How much to remove per day before date + var/ramp_chance = 0.5 + /// How much to scale cobwebs alpha down per day (1 - ramp_scale * days, affects alpha & size) + var/ramp_scale = 0.01 + /// Extra randomness removed onto scale before full blown halloween + var/scale_rand = 0.3 + +/datum/game_decorator/halloween/cobwebs/decorate() + for(var/turf/closed/wall/almayer/turf in world) + if(is_mainship_level(turf.z)) + decorate_turf(turf) + CHECK_TICK + +/datum/game_decorator/halloween/cobwebs/proc/decorate_turf(turf/closed/wall/almayer/turf) + var/static/list/order = list(NORTHWEST, SOUTHEAST, NORTHEAST, SOUTHWEST) // Ordering of wall_connections + if(length(turf.wall_connections) < 4) + return + + var/event_progress = get_days_remaining() + var/placement_chance = base_chance - (event_progress * ramp_chance) + for(var/i = 1 to 4) + var/diag = order[i] + if(turf.wall_connections[i] != "5") // CORNER_CLOCKWISE | CORNER_COUNTERCLOCKWISE as string - don't ask me + continue + if(!prob(placement_chance)) + continue + + // Skip this if this corner is result of a door connection (mostly for Almayer shutters) + var/valid = TRUE + for(var/a_cardinal in cardinal) + var/cardinal_dir = diag & a_cardinal + if(!a_cardinal) // We check cardinals contributing to that diagonal + continue + var/turf/target = get_step(turf, cardinal_dir) + if(locate(/obj/structure/machinery/door) in target) + valid = FALSE + break + + if(valid) // Actually place cobweb + var/turf/target = get_step(turf, diag) + if(istype(target, /turf/open)) + var/scale = 1 - ramp_scale * event_progress + scale -= scale_rand * rand() + new /obj/effect/decal/cleanable/cobweb2/dynamic(target, diag, scale) + diff --git a/code/modules/holidays/halloween/pumpkins/patches.dm b/code/modules/holidays/halloween/pumpkins/patches.dm new file mode 100644 index 000000000000..b84ba24ef4e6 --- /dev/null +++ b/code/modules/holidays/halloween/pumpkins/patches.dm @@ -0,0 +1,65 @@ +/// Patches of pumpkins spawned at roundstart from where marines can get their carvable pumpkins +/obj/structure/pumpkin_patch + icon = 'icons/misc/events/pumpkins.dmi' + name = "patch of pumpkins" + var/empty_name = "\proper vines" + + can_block_movement = FALSE + unslashable = TRUE + health = 400 // To avoid explosions and stray gunfire destroying them too easily + layer = LOWER_ITEM_LAYER + + var/has_vines = TRUE //! Whether there's still vines to display or not + var/pumpkin_count = 3 //! Amount of pumpkins currently in the patch + var/icon_prefix //! Prefix to prepend to icon states, for corrupted pumpkins + var/pumpkin_type = /obj/item/clothing/head/pumpkin + +/obj/structure/pumpkin_patch/Initialize(mapload, ...) + . = ..() + update_icon() + +/obj/structure/pumpkin_patch/update_icon() + overlays?.Cut() + . = ..() + switch(pumpkin_count) + if(3) icon_state = "[icon_prefix]pumpkins_full" + if(2) icon_state = "[icon_prefix]pumpkins_half" + if(1) icon_state = "[icon_prefix]pumpkin" + else icon_state = "empty" + if(has_vines) + overlays += image(icon, loc, "[icon_prefix]vines") + +/obj/structure/pumpkin_patch/attack_hand(mob/user) + if(pumpkin_count < 1) + to_chat(user, SPAN_WARNING("No more pumpkins here...")) + return + if(!user.get_active_hand()) //if active hand is empty + pumpkin_count-- + var/obj/item/clothing/head/pumpkin/pumpkin = new pumpkin_type(loc) + user.put_in_hands(pumpkin) + playsound(loc, 'sound/effects/vegetation_hit.ogg', 25, 1) + update_icon() + if(pumpkin_count < 1) + if(!has_vines) + qdel(src) + else + name = empty_name + return + return ..() + +/obj/structure/pumpkin_patch/attackby(obj/item/tool, mob/user) + if(has_vines && (tool.sharp == IS_SHARP_ITEM_ACCURATE || tool.sharp == IS_SHARP_ITEM_BIG)) + to_chat(user, SPAN_NOTICE("You cut down the vines.")) + playsound(loc, "alien_resin_break", 25) + has_vines = FALSE + update_icon() + if(pumpkin_count < 1 && !has_vines) + qdel(src) + return + return ..() + +/obj/structure/pumpkin_patch/corrupted + icon_prefix = "cor_" + name = "patch of corrupted pumpkins" + empty_name = "\proper corrupted vines" + pumpkin_type = /obj/item/clothing/head/pumpkin/corrupted diff --git a/code/modules/holidays/halloween/pumpkins/wearable.dm b/code/modules/holidays/halloween/pumpkins/wearable.dm new file mode 100644 index 000000000000..74db2b4c85ef --- /dev/null +++ b/code/modules/holidays/halloween/pumpkins/wearable.dm @@ -0,0 +1,72 @@ +/// Carved Pumpkin from the Halloween event +/obj/item/clothing/head/pumpkin + name = "pumpkin" + icon = 'icons/misc/events/pumpkins.dmi' + item_icons = list( + WEAR_HEAD = 'icons/misc/events/pumpkins.dmi', + ) + desc = "An ominous looking pumpkin. Would look pretty spooky if worn on your head..." + w_class = SIZE_MEDIUM + flags_inv_hide = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEALLHAIR + flags_armor_protection = BODY_FLAG_HEAD|BODY_FLAG_EYES + flags_inventory = COVEREYES|BLOCKSHARPOBJ|COVERMOUTH + flags_cold_protection = BODY_FLAG_HEAD + flags_heat_protection = BODY_FLAG_HEAD + min_cold_protection_temperature = HELMET_MIN_COLD_PROT + max_heat_protection_temperature = HELMET_MAX_HEAT_PROT + armor_melee = CLOTHING_ARMOR_MEDIUM + armor_bullet = CLOTHING_ARMOR_MEDIUM + armor_laser = CLOTHING_ARMOR_MEDIUMLOW + armor_energy = CLOTHING_ARMOR_NONE + armor_bomb = CLOTHING_ARMOR_LOW + armor_bio = CLOTHING_ARMOR_MEDIUM + armor_rad = CLOTHING_ARMOR_LOW + armor_internaldamage = CLOTHING_ARMOR_MEDIUM + health = 5 + force = 15 + var/prefix = "" //! Icon state prefix for corrupted pumpkin variants + var/carved_icon = "" //! Currently carved pumpkin overlay + var/carvable_icons = list("smile", "cheeky", "bugeyes", "upside_down_smile", "skelly", "ff") + +/obj/item/clothing/head/pumpkin/Initialize(mapload, ...) + . = ..() + update_icon() + +/obj/item/clothing/head/pumpkin/update_icon() + . = ..() + if(carved_icon) + icon_state = "[prefix]pumpkin_carved" + else + icon_state = "[prefix]pumpkin" + item_state_slots = list( + WEAR_HEAD = "[prefix]pumpkin_onmob", + ) + +/obj/item/clothing/head/pumpkin/mob_can_equip(mob/user, slot, disable_warning) + if(slot == WEAR_HEAD && !carved_icon) + to_chat(user, SPAN_WARNING("You can't put on a full pumpkin! Empty and carve it with a sharp object first.")) + return FALSE + . = ..() + +/obj/item/clothing/head/pumpkin/attackby(obj/item/tool, mob/user) + if(!carved_icon && (tool.sharp == IS_SHARP_ITEM_ACCURATE || tool.sharp == IS_SHARP_ITEM_BIG)) + var/choice = tgui_input_list(user, "Select the pattern to carve on your pumpkin!", "Pumpkin Carving", carvable_icons) + if(choice) + playsound(loc, 'sound/effects/vegetation_hit.ogg', 25, 1) + carved_icon = choice + name = "carved pumpkin" + update_icon() + else + return ..() + +/obj/item/clothing/head/pumpkin/get_mob_overlay(mob/user_mob, slot) + var/image/pumpkin = ..() + if(carved_icon && slot == WEAR_HEAD) + var/image/overlay = overlay_image(icon, "[prefix]pumpkin_[carved_icon]") + pumpkin.overlays += overlay + return pumpkin + +/obj/item/clothing/head/pumpkin/corrupted + name = "corrupted pumpkin" + prefix = "cor_" + carvable_icons = list("cry", "sob", "sad", "why", "spooky", "ff") diff --git a/colonialmarines.dme b/colonialmarines.dme index 37c34d443c1b..25aa4df30a09 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -238,6 +238,7 @@ #include "code\controllers\subsystem\disease.dm" #include "code\controllers\subsystem\events.dm" #include "code\controllers\subsystem\fz_transitions.dm" +#include "code\controllers\subsystem\game_decorator.dm" #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\human.dm" #include "code\controllers\subsystem\inactivity.dm" @@ -1627,7 +1628,6 @@ #include "code\modules\decorators\admin_runtime_decorator.dm" #include "code\modules\decorators\cassette_decorator.dm" #include "code\modules\decorators\christmas.dm" -#include "code\modules\decorators\halloween.dm" #include "code\modules\decorators\mass_xeno_decorator.dm" #include "code\modules\decorators\weapon_decorator.dm" #include "code\modules\decorators\weapon_map_decorator.dm" @@ -1723,6 +1723,9 @@ #include "code\modules\gear_presets\survivors\sorokyne_strata\preset_sorokyne_strata.dm" #include "code\modules\gear_presets\survivors\trijent\crashlanding_upp_bar_insert_trijent.dm" #include "code\modules\gear_presets\survivors\trijent\preset_trijent.dm" +#include "code\modules\holidays\halloween\decorators.dm" +#include "code\modules\holidays\halloween\pumpkins\patches.dm" +#include "code\modules\holidays\halloween\pumpkins\wearable.dm" #include "code\modules\hydroponics\botany_disks.dm" #include "code\modules\hydroponics\grown_inedible.dm" #include "code\modules\hydroponics\hydro_tools.dm" diff --git a/icons/misc/events/pumpkins.dmi b/icons/misc/events/pumpkins.dmi new file mode 100644 index 000000000000..e3087e7c444c Binary files /dev/null and b/icons/misc/events/pumpkins.dmi differ