diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 09d596cb657..c1956bc3898 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -34,17 +34,28 @@ actual development. * Pull requests should be atomic; Make one commit for each distinct change, so if a part of a pull request needs to be removed/changed, you may simply modify that single commit. Due to limitations of the engine, this may not always be possible; but do try your best. - * Document and explain your pull requests thoroughly. Detail what each commit changes, - and why it changes it. We do not want to have to read all of you commit names to figure - out what your pull request is about. + + * Document and explain your pull requests thoroughly. Failure to do so will delay a PR as + we question why changes were made. This is especially important if you're porting a PR + from another codebase (i.e. TG) and divert from the original. Explaining with single + comment on why you've made changes will help us review the PR faster and understand your + decision making process. + * Any pull request must have a changelog, this is to allow us to know when a PR is deployed on the live server. Inline changelogs are supported through the format described [here](https://github.com/ParadiseSS13/Paradise/pull/3291#issuecomment-172950466) and should be used rather than manually edited .yml file changelogs. + * Pull requests should not have any merge commits except in the case of fixing merge conflicts for an existing pull request. New pull requests should not have any merge commits. Use `git rebase` or `git reset` to update your branches, not `git pull`. + * Please explain why you are submitting the pull request, and how you think your change will be beneficial to the game. Failure to do so will be grounds for rejecting the PR. + + * If your pull request is not finished make sure it is at least testable in a live environment. Pull requests that do not at least meet this requirement may be closed at maintainer discretion. You may request a maintainer reopen the pull request when you're ready, or make a new one. + + * While we have no issue helping contributors (and especially new contributors) bring reasonably sized contributions up to standards via the pull request review process, larger contributions are expected to pass a higher bar of completeness and code quality *before* you open a pull request. Maintainers may close such pull requests that are deemed to be substantially flawed. You should take some time to discuss with maintainers or other contributors on how to improve the changes. + #### Using Changelog * Tags used in changelog include add/rscadd, del/rscdel, fix/fixes, typo/spellcheck. * Without specifying a name it will default to using your GitHub name. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b3f8516d44e..45b7dc5a642 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,11 +1,16 @@ -**What does this PR do:** -Include a small to medium description of what your PR changes. Feel free to give it a little flourish if you want to show off a new feature more completely. + + -**Images of sprite/map changes (IF APPLICABLE):** -If you did not make a map or sprite edit, you may leave this section blank. You may include a gif of your feature if you want +## What Does This PR Do + -**Changelog:** -*Remove this line, and appropriate fields from the changelog* +## Why It's Good For The Game + + +## Images of changes + + +## Changelog :cl: add: Added new things del: Removed old things @@ -21,3 +26,5 @@ spellcheck: fixed a few typos experiment: added an experimental thingy /:cl: + + diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 4ecfe95d24c..b7221f581b9 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -298,7 +298,7 @@ This is always put in the attack log. loglevel = ATKLOG_FEW else if(istype(user) && !user.ckey && !target.ckey) // Attacks between NPCs are only shown to admins with ATKLOG_ALL loglevel = ATKLOG_ALL - else if(!user.ckey || !target.ckey) // Player v NPC combat is de-prioritized. + else if(!user.ckey || !target.ckey || (user.ckey == target.ckey)) // Player v NPC combat is de-prioritized. Also no self-harm, nobody cares loglevel = ATKLOG_ALMOSTALL else var/area/A = get_area(target) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index acfcda5c40b..8ae35d52179 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -244,6 +244,9 @@ //cube monkey limit var/cubemonkeycap = 20 + + // Makes gamemodes respect player limits + var/enable_gamemode_player_limit = 0 /datum/configuration/New() for(var/T in subtypesof(/datum/game_mode)) @@ -723,6 +726,8 @@ config.developer_express_start = 1 if("disable_localhost_admin") config.disable_localhost_admin = 1 + if("enable_gamemode_player_limit") + config.enable_gamemode_player_limit = 1 else log_config("Unknown setting in configuration: '[name]'") diff --git a/code/datums/action.dm b/code/datums/action.dm index 6a2f02c2562..043117efb84 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -212,7 +212,7 @@ /datum/action/item_action/toggle_unfriendly_fire name = "Toggle Friendly Fire \[ON\]" - desc = "Toggles if the staff causes friendly fire." + desc = "Toggles if the club's blasts cause friendly fire." button_icon_state = "vortex_ff_on" /datum/action/item_action/toggle_unfriendly_fire/Trigger() @@ -220,8 +220,8 @@ UpdateButtonIcon() /datum/action/item_action/toggle_unfriendly_fire/UpdateButtonIcon() - if(istype(target, /obj/item/hierophant_staff)) - var/obj/item/hierophant_staff/H = target + if(istype(target, /obj/item/hierophant_club)) + var/obj/item/hierophant_club/H = target if(H.friendly_fire_check) button_icon_state = "vortex_ff_off" name = "Toggle Friendly Fire \[OFF\]" @@ -247,12 +247,12 @@ /datum/action/item_action/vortex_recall name = "Vortex Recall" - desc = "Recall yourself, and anyone nearby, to an attuned hierophant rune at any time.
If no such rune exists, will produce a rune at your location." + desc = "Recall yourself, and anyone nearby, to an attuned hierophant beacon at any time.
If the beacon is still attached, will detach it." button_icon_state = "vortex_recall" /datum/action/item_action/vortex_recall/IsAvailable() - if(istype(target, /obj/item/hierophant_staff)) - var/obj/item/hierophant_staff/H = target + if(istype(target, /obj/item/hierophant_club)) + var/obj/item/hierophant_club/H = target if(H.teleporting) return 0 return ..() diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm index 7fe8ad7248e..bb80fe84d01 100644 --- a/code/datums/ruins/lavaland.dm +++ b/code/datums/ruins/lavaland.dm @@ -124,8 +124,8 @@ datum/map_template/ruin/lavaland/ash_walker id = "hierophant" description = "A strange, square chunk of metal of massive size. Inside awaits only death and many, many squares." suffix = "lavaland_surface_hierophant.dmm" - allow_duplicates = FALSE always_place = TRUE + allow_duplicates = FALSE /datum/map_template/ruin/lavaland/blood_drunk_miner name = "Blood-Drunk Miner" diff --git a/code/datums/spells/wizard.dm b/code/datums/spells/wizard.dm index 739a03d92c5..6bcba4cd83b 100644 --- a/code/datums/spells/wizard.dm +++ b/code/datums/spells/wizard.dm @@ -383,18 +383,18 @@ name = "Repulse" desc = "This spell throws everything around the user away." charge_max = 400 - clothes_req = 1 + clothes_req = TRUE invocation = "GITTAH WEIGH" invocation_type = "shout" range = 5 cooldown_min = 150 selection_type = "view" + sound = 'sound/magic/repulse.ogg' var/maxthrow = 5 var/sparkle_path = /obj/effect/temp_visual/gravpush action_icon_state = "repulse" - sound = 'sound/magic/repulse.ogg' -/obj/effect/proc_holder/spell/aoe_turf/repulse/cast(list/targets, mob/user = usr) +/obj/effect/proc_holder/spell/aoe_turf/repulse/cast(list/targets, mob/user = usr, stun_amt = 2) var/list/thrownatoms = list() var/atom/throwtarget var/distfromcaster @@ -411,16 +411,16 @@ throwtarget = get_edge_target_turf(user, get_dir(user, get_step_away(AM, user))) distfromcaster = get_dist(user, AM) if(distfromcaster == 0) - if(istype(AM, /mob/living)) + if(isliving(AM)) var/mob/living/M = AM M.Weaken(5) M.adjustBruteLoss(5) to_chat(M, "You're slammed into the floor by a mystical force!") else new sparkle_path(get_turf(AM), get_dir(user, AM)) //created sparkles will disappear on their own - if(istype(AM, /mob/living)) + if(isliving(AM)) var/mob/living/M = AM - M.Weaken(2) + M.Weaken(stun_amt) to_chat(M, "You're thrown back by a mystical force!") spawn(0) AM.throw_at(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1)//So stuff gets tossed around at the same time. diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm index 75e37c3ca3d..a62155eb10d 100644 --- a/code/datums/uplink_item.dm +++ b/code/datums/uplink_item.dm @@ -1037,7 +1037,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/explosives/grenadier name = "Grenadier's belt" - desc = "A belt containing 25 lethally dangerous and destructive grenades." + desc = "A belt containing 26 lethally dangerous and destructive grenades." item = /obj/item/storage/belt/grenade/full cost = 30 surplus = 0 @@ -1052,15 +1052,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) gamemodes = list(/datum/game_mode/nuclear) surplus = 35 -/datum/uplink_item/explosives/saringrenades - name = "Sarin Gas Grenades" - desc = "A box of four (4) grenades filled with Sarin, a deadly neurotoxin. Use extreme caution when handling and be sure to vacate the premise after using; ensure communication is maintained with team to avoid accidental gassings." - reference = "TGG" - item = /obj/item/storage/box/syndie_kit/sarin - cost = 12 - gamemodes = list(/datum/game_mode/nuclear) - surplus = 0 - /datum/uplink_item/explosives/atmosn2ogrenades name = "Knockout Gas Grenades" desc = "A box of two (2) grenades that spread knockout gas over a large area. Equip internals before using one of these." @@ -1651,10 +1642,10 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/bundles_TC/c20r name = "C-20r Bundle" - desc = "Old Faithful: The classic C-20r, bundled with two magazines and a (surplus) suppressor at discount price." + desc = "Old Faithful: The classic C-20r, bundled with three magazines and a (surplus) suppressor at discount price." reference = "C20B" item = /obj/item/storage/backpack/duffel/syndie/c20rbundle - cost = 14 // normally 17 + cost = 18 // normally 21 gamemodes = list(/datum/game_mode/nuclear) /datum/uplink_item/bundles_TC/cyber_implants @@ -1669,10 +1660,10 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/bundles_TC/medical name = "Medical Bundle" desc = "The support specialist: Aid your fellow operatives with this medical bundle. Contains a tactical medkit, \ - a Donksoft LMG and a box of riot darts." + a medical beam gun and a pair of syndicate magboots." reference = "MEDB" item = /obj/item/storage/backpack/duffel/syndie/med/medicalbundle - cost = 15 // normally 20 + cost = 20 // normally 24 gamemodes = list(/datum/game_mode/nuclear) /datum/uplink_item/bundles_TC/sniper diff --git a/code/game/gamemodes/blob/blob.dm b/code/game/gamemodes/blob/blob.dm index df61106c4ab..33b68f94079 100644 --- a/code/game/gamemodes/blob/blob.dm +++ b/code/game/gamemodes/blob/blob.dm @@ -104,6 +104,7 @@ var/list/blob_nodes = list() to_chat(blob.current, "Find a good location to spawn the core and then take control and overwhelm the station!") to_chat(blob.current, "When you have found a location, wait until you spawn; this will happen automatically and you cannot speed up the process.") to_chat(blob.current, "If you go outside of the station level, or in space, then you will die; make sure your location has lots of ground to cover.") + SEND_SOUND(blob.current, 'sound/magic/mutate.ogg') return /datum/game_mode/blob/proc/show_message(var/message) @@ -207,4 +208,4 @@ var/list/blob_nodes = list() /datum/game_mode/proc/update_blob_icons_removed(datum/mind/mob_mind) var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_BLOB] antaghud.leave_hud(mob_mind.current) - set_antag_hud(mob_mind.current, null) \ No newline at end of file + set_antag_hud(mob_mind.current, null) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index aa366415f31..e7fb9b4969f 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -50,7 +50,7 @@ if((player.client)&&(player.ready)) playerC++ - if(playerC >= required_players) + if(!config.enable_gamemode_player_limit || (playerC >= required_players)) return 1 return 0 diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm index 3491c043b4c..232396d951a 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm @@ -82,7 +82,6 @@ /obj/item/clothing/suit/armor/abductor/vest/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) DeactivateStealth() - return 0 /obj/item/clothing/suit/armor/abductor/vest/IsReflect() DeactivateStealth() diff --git a/code/game/gamemodes/miniantags/guardian/types/assassin.dm b/code/game/gamemodes/miniantags/guardian/types/assassin.dm index e9ea9239483..4b17e597aa5 100644 --- a/code/game/gamemodes/miniantags/guardian/types/assassin.dm +++ b/code/game/gamemodes/miniantags/guardian/types/assassin.dm @@ -2,7 +2,6 @@ melee_damage_lower = 15 melee_damage_upper = 15 armour_penetration = 0 - damage_transfer = 0.6 playstyle_string = "As an Assassin type you do medium damage and have no damage resistance, but can enter stealth, massively increasing the damage of your next attack and causing it to ignore armor. Stealth is broken when you attack or take damage." magic_fluff_string = "..And draw the Space Ninja, a lethal, invisible assassin." tech_fluff_string = "Boot sequence complete. Assassin modules loaded. Holoparasite swarm online." diff --git a/code/game/machinery/poolcontroller.dm b/code/game/machinery/poolcontroller.dm index 0d6f76aad38..4b512297ffd 100644 --- a/code/game/machinery/poolcontroller.dm +++ b/code/game/machinery/poolcontroller.dm @@ -116,7 +116,7 @@ if(!drownee) return - if(drownee && (drownee.lying || deep_water)) //Mob lying down or water is deep (determined by controller) + if(drownee && ((drownee.lying && !drownee.player_logged) || deep_water)) //Mob lying down and not SSD or water is deep (determined by controller) if(drownee.internal) return //Has internals, no drowning if((NO_BREATHE in drownee.dna.species.species_traits) || (BREATHLESS in drownee.mutations)) diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 2c76308c46c..d85aaa651cc 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -121,9 +121,10 @@ product_records = list() hidden_records = list() coin_records = list() - build_inventory(products, product_records, start_empty = TRUE) - build_inventory(contraband, hidden_records, start_empty = TRUE) - build_inventory(premium, coin_records, start_empty = TRUE) + if(refill_canister) + build_inventory(products, product_records, start_empty = TRUE) + build_inventory(contraband, hidden_records, start_empty = TRUE) + build_inventory(premium, coin_records, start_empty = TRUE) for(var/obj/item/vending_refill/VR in component_parts) restock(VR) @@ -1275,16 +1276,57 @@ product_slogans = "THIS'S WHERE TH' SEEDS LIVE! GIT YOU SOME!;Hands down the best seed selection on the station!;Also certain mushroom varieties available, more for experts! Get certified today!" product_ads = "We like plants!;Grow some crops!;Grow, baby, growww!;Aw h'yeah son!" icon_state = "seeds" - products = list(/obj/item/seeds/aloe =3, /obj/item/seeds/ambrosia = 3, /obj/item/seeds/apple = 3, /obj/item/seeds/cotton = 3, /obj/item/seeds/banana = 3, /obj/item/seeds/berry = 3, - /obj/item/seeds/cabbage = 3, /obj/item/seeds/carrot = 3, /obj/item/seeds/cherry = 3, /obj/item/seeds/chanter = 3, - /obj/item/seeds/chili = 3, /obj/item/seeds/cocoapod = 3, /obj/item/seeds/coffee = 3, /obj/item/seeds/comfrey =3, /obj/item/seeds/corn = 3, - /obj/item/seeds/nymph =3, /obj/item/seeds/eggplant = 3, /obj/item/seeds/grape = 3, /obj/item/seeds/grass = 3, /obj/item/seeds/lemon = 3, - /obj/item/seeds/lime = 3, /obj/item/seeds/onion = 3, /obj/item/seeds/orange = 3, /obj/item/seeds/peanuts = 3, /obj/item/seeds/pineapple = 3, /obj/item/seeds/potato = 3, /obj/item/seeds/poppy = 3, - /obj/item/seeds/pumpkin = 3, /obj/item/seeds/replicapod = 3, /obj/item/seeds/wheat/rice = 3, /obj/item/seeds/soya = 3, /obj/item/seeds/sunflower = 3, /obj/item/seeds/sugarcane = 3, - /obj/item/seeds/tea = 3, /obj/item/seeds/tobacco = 3, /obj/item/seeds/tomato = 3, - /obj/item/seeds/tower = 3, /obj/item/seeds/watermelon = 3, /obj/item/seeds/wheat = 3, /obj/item/seeds/whitebeet = 3) - contraband = list(/obj/item/seeds/amanita = 2, /obj/item/seeds/glowshroom = 2, /obj/item/seeds/liberty = 2, /obj/item/seeds/nettle = 2, - /obj/item/seeds/plump = 2, /obj/item/seeds/reishi = 2, /obj/item/seeds/cannabis = 3, /obj/item/seeds/starthistle = 2, /obj/item/seeds/fungus = 3, /obj/item/seeds/random = 2) + products = list(/obj/item/seeds/aloe =3, + /obj/item/seeds/ambrosia = 3, + /obj/item/seeds/apple = 3, + /obj/item/seeds/cotton = 3, + /obj/item/seeds/banana = 3, + /obj/item/seeds/berry = 3, + /obj/item/seeds/cabbage = 3, + /obj/item/seeds/carrot = 3, + /obj/item/seeds/cherry = 3, + /obj/item/seeds/chanter = 3, + /obj/item/seeds/chili = 3, + /obj/item/seeds/cocoapod = 3, + /obj/item/seeds/coffee = 3, + /obj/item/seeds/comfrey =3, + /obj/item/seeds/corn = 3, + /obj/item/seeds/nymph =3, + /obj/item/seeds/eggplant = 3, + /obj/item/seeds/garlic = 3, + /obj/item/seeds/grape = 3, + /obj/item/seeds/grass = 3, + /obj/item/seeds/lemon = 3, + /obj/item/seeds/lime = 3, + /obj/item/seeds/onion = 3, + /obj/item/seeds/orange = 3, + /obj/item/seeds/peanuts = 3, + /obj/item/seeds/pineapple = 3, + /obj/item/seeds/potato = 3, + /obj/item/seeds/poppy = 3, + /obj/item/seeds/pumpkin = 3, + /obj/item/seeds/replicapod = 3, + /obj/item/seeds/wheat/rice = 3, + /obj/item/seeds/soya = 3, + /obj/item/seeds/sunflower = 3, + /obj/item/seeds/sugarcane = 3, + /obj/item/seeds/tea = 3, + /obj/item/seeds/tobacco = 3, + /obj/item/seeds/tomato = 3, + /obj/item/seeds/tower = 3, + /obj/item/seeds/watermelon = 3, + /obj/item/seeds/wheat = 3, + /obj/item/seeds/whitebeet = 3) + contraband = list(/obj/item/seeds/amanita = 2, + /obj/item/seeds/glowshroom = 2, + /obj/item/seeds/liberty = 2, + /obj/item/seeds/nettle = 2, + /obj/item/seeds/plump = 2, + /obj/item/seeds/reishi = 2, + /obj/item/seeds/cannabis = 3, + /obj/item/seeds/starthistle = 2, + /obj/item/seeds/fungus = 3, + /obj/item/seeds/random = 2) premium = list(/obj/item/reagent_containers/spray/waterflower = 1) refill_canister = /obj/item/vending_refill/hydroseeds @@ -1326,41 +1368,129 @@ product_slogans = "Dress for success!;Suited and booted!;It's show time!;Why leave style up to fate? Use AutoDrobe!" vend_delay = 15 vend_reply = "Thank you for using AutoDrobe!" - products = list(/obj/item/clothing/suit/chickensuit = 1,/obj/item/clothing/head/chicken = 1,/obj/item/clothing/under/gladiator = 1, - /obj/item/clothing/head/helmet/gladiator = 1,/obj/item/clothing/under/gimmick/rank/captain/suit = 1,/obj/item/clothing/head/flatcap = 1, - /obj/item/clothing/suit/storage/labcoat/mad = 1,/obj/item/clothing/glasses/gglasses = 1,/obj/item/clothing/shoes/jackboots = 1, - /obj/item/clothing/under/schoolgirl = 1,/obj/item/clothing/head/kitty = 1,/obj/item/clothing/under/blackskirt = 1, - /obj/item/clothing/suit/toggle/owlwings = 1, /obj/item/clothing/under/owl = 1,/obj/item/clothing/mask/gas/owl_mask = 1, - /obj/item/clothing/suit/toggle/owlwings/griffinwings = 1, /obj/item/clothing/under/griffin = 1, /obj/item/clothing/shoes/griffin = 1, /obj/item/clothing/head/griffin = 1, - /obj/item/clothing/accessory/waistcoat = 1,/obj/item/clothing/under/suit_jacket = 1,/obj/item/clothing/head/that =1,/obj/item/clothing/under/kilt = 1,/obj/item/clothing/accessory/waistcoat = 1, - /obj/item/clothing/glasses/monocle =1,/obj/item/clothing/head/bowlerhat = 1,/obj/item/cane = 1,/obj/item/clothing/under/sl_suit = 1, - /obj/item/clothing/mask/fakemoustache = 1,/obj/item/clothing/suit/bio_suit/plaguedoctorsuit = 1,/obj/item/clothing/head/plaguedoctorhat = 1,/obj/item/clothing/mask/gas/plaguedoctor = 1, - /obj/item/clothing/suit/apron = 1,/obj/item/clothing/under/waiter = 1,/obj/item/clothing/suit/jacket/miljacket = 1, - /obj/item/clothing/suit/jacket/miljacket/white = 1, /obj/item/clothing/suit/jacket/miljacket/desert = 1, /obj/item/clothing/suit/jacket/miljacket/navy = 1, - /obj/item/clothing/under/pirate = 1,/obj/item/clothing/suit/pirate_brown = 1,/obj/item/clothing/suit/pirate_black =1,/obj/item/clothing/under/pirate_rags =1,/obj/item/clothing/head/pirate = 1,/obj/item/clothing/head/bandana = 1, - /obj/item/clothing/head/bandana = 1,/obj/item/clothing/under/soviet = 1,/obj/item/clothing/head/ushanka = 1,/obj/item/clothing/suit/imperium_monk = 1, - /obj/item/clothing/mask/gas/cyborg = 1,/obj/item/clothing/suit/holidaypriest = 1,/obj/item/clothing/head/wizard/marisa/fake = 1, - /obj/item/clothing/suit/wizrobe/marisa/fake = 1,/obj/item/clothing/under/sundress = 1,/obj/item/clothing/head/witchwig = 1,/obj/item/twohanded/staff/broom = 1, - /obj/item/clothing/suit/wizrobe/fake = 1,/obj/item/clothing/head/wizard/fake = 1,/obj/item/twohanded/staff = 3,/obj/item/clothing/mask/gas/clown_hat/sexy = 1, - /obj/item/clothing/under/rank/clown/sexy = 1,/obj/item/clothing/mask/gas/sexymime = 1,/obj/item/clothing/under/sexymime = 1, - /obj/item/clothing/mask/face/bat = 1,/obj/item/clothing/mask/face/bee = 1,/obj/item/clothing/mask/face/bear = 1,/obj/item/clothing/mask/face/raven = 1,/obj/item/clothing/mask/face/jackal = 1,/obj/item/clothing/mask/face/fox = 1,/obj/item/clothing/mask/face/tribal = 1,/obj/item/clothing/mask/face/rat = 1, + products = list(/obj/item/clothing/suit/chickensuit = 1, + /obj/item/clothing/head/chicken = 1, + /obj/item/clothing/under/gladiator = 1, + /obj/item/clothing/head/helmet/gladiator = 1, + /obj/item/clothing/under/gimmick/rank/captain/suit = 1, + /obj/item/clothing/head/flatcap = 1, + /obj/item/clothing/suit/storage/labcoat/mad = 1, + /obj/item/clothing/glasses/gglasses = 1, + /obj/item/clothing/shoes/jackboots = 1, + /obj/item/clothing/under/schoolgirl = 1, + /obj/item/clothing/head/kitty = 1, + /obj/item/clothing/under/blackskirt = 1, + /obj/item/clothing/suit/toggle/owlwings = 1, + /obj/item/clothing/under/owl = 1, + /obj/item/clothing/mask/gas/owl_mask = 1, + /obj/item/clothing/suit/toggle/owlwings/griffinwings = 1, + /obj/item/clothing/under/griffin = 1, + /obj/item/clothing/shoes/griffin = 1, + /obj/item/clothing/head/griffin = 1, + /obj/item/clothing/accessory/waistcoat = 1, + /obj/item/clothing/under/suit_jacket = 1, + /obj/item/clothing/head/that =1, + /obj/item/clothing/under/kilt = 1, + /obj/item/clothing/accessory/waistcoat = 1, + /obj/item/clothing/glasses/monocle =1, + /obj/item/clothing/head/bowlerhat = 1, + /obj/item/cane = 1, + /obj/item/clothing/under/sl_suit = 1, + /obj/item/clothing/mask/fakemoustache = 1, + /obj/item/clothing/suit/bio_suit/plaguedoctorsuit = 1, + /obj/item/clothing/head/plaguedoctorhat = 1, + /obj/item/clothing/mask/gas/plaguedoctor = 1, + /obj/item/clothing/suit/apron = 1, + /obj/item/clothing/under/waiter = 1, + /obj/item/clothing/suit/jacket/miljacket = 1, + /obj/item/clothing/suit/jacket/miljacket/white = 1, + /obj/item/clothing/suit/jacket/miljacket/desert = 1, + /obj/item/clothing/suit/jacket/miljacket/navy = 1, + /obj/item/clothing/under/pirate = 1, + /obj/item/clothing/suit/pirate_brown = 1, + /obj/item/clothing/suit/pirate_black =1, + /obj/item/clothing/under/pirate_rags =1, + /obj/item/clothing/head/pirate = 1, + /obj/item/clothing/head/bandana = 1, + /obj/item/clothing/head/bandana = 1, + /obj/item/clothing/under/soviet = 1, + /obj/item/clothing/head/ushanka = 1, + /obj/item/clothing/suit/imperium_monk = 1, + /obj/item/clothing/mask/gas/cyborg = 1, + /obj/item/clothing/suit/holidaypriest = 1, + /obj/item/clothing/head/wizard/marisa/fake = 1, + /obj/item/clothing/suit/wizrobe/marisa/fake = 1, + /obj/item/clothing/under/sundress = 1, + /obj/item/clothing/head/witchwig = 1, + /obj/item/twohanded/staff/broom = 1, + /obj/item/clothing/suit/wizrobe/fake = 1, + /obj/item/clothing/head/wizard/fake = 1, + /obj/item/twohanded/staff = 3, + /obj/item/clothing/mask/gas/clown_hat/sexy = 1, + /obj/item/clothing/under/rank/clown/sexy = 1, + /obj/item/clothing/mask/gas/sexymime = 1, + /obj/item/clothing/under/sexymime = 1, + /obj/item/clothing/mask/face/bat = 1, + /obj/item/clothing/mask/face/bee = 1, + /obj/item/clothing/mask/face/bear = 1, + /obj/item/clothing/mask/face/raven = 1, + /obj/item/clothing/mask/face/jackal = 1, + /obj/item/clothing/mask/face/fox = 1, + /obj/item/clothing/mask/face/tribal = 1, + /obj/item/clothing/mask/face/rat = 1, /obj/item/clothing/suit/apron/overalls = 1, - /obj/item/clothing/head/rabbitears =1, /obj/item/clothing/head/sombrero = 1, /obj/item/clothing/suit/poncho = 1, - /obj/item/clothing/suit/poncho/green = 1, /obj/item/clothing/suit/poncho/red = 1, /obj/item/clothing/accessory/blue = 1, /obj/item/clothing/accessory/red = 1, /obj/item/clothing/accessory/black = 1, /obj/item/clothing/accessory/horrible = 1, - /obj/item/clothing/under/maid = 1, /obj/item/clothing/under/janimaid = 1, - /obj/item/clothing/under/jester = 1, /obj/item/clothing/head/jester = 1, - /obj/item/clothing/under/pants/camo = 1, /obj/item/clothing/mask/bandana = 1, /obj/item/clothing/mask/bandana/black = 1, - /obj/item/clothing/shoes/singery = 1,/obj/item/clothing/under/singery = 1, - /obj/item/clothing/shoes/singerb = 1,/obj/item/clothing/under/singerb = 1, - /obj/item/clothing/suit/hooded/carp_costume = 1,/obj/item/clothing/suit/hooded/bee_costume = 1, - /obj/item/clothing/suit/snowman = 1,/obj/item/clothing/head/snowman = 1, - /obj/item/clothing/head/cueball = 1,/obj/item/clothing/under/scratch = 1, - /obj/item/clothing/under/victdress = 1, /obj/item/clothing/under/victdress/red = 1, /obj/item/clothing/suit/victcoat = 1, /obj/item/clothing/suit/victcoat/red = 1, - /obj/item/clothing/under/victsuit = 1, /obj/item/clothing/under/victsuit/redblk = 1, /obj/item/clothing/under/victsuit/red = 1, /obj/item/clothing/suit/tailcoat = 1, - /obj/item/clothing/suit/draculacoat = 1, /obj/item/clothing/head/zepelli = 1, - /obj/item/clothing/under/redhawaiianshirt = 1, /obj/item/clothing/under/pinkhawaiianshirt = 1, /obj/item/clothing/under/bluehawaiianshirt = 1, /obj/item/clothing/under/orangehawaiianshirt = 1) - contraband = list(/obj/item/clothing/suit/judgerobe = 1,/obj/item/clothing/head/powdered_wig = 1,/obj/item/gun/magic/wand = 1, /obj/item/clothing/mask/balaclava=1, /obj/item/clothing/mask/horsehead = 2) - premium = list(/obj/item/clothing/suit/hgpirate = 1, /obj/item/clothing/head/hgpiratecap = 1, /obj/item/clothing/head/helmet/roman = 1, /obj/item/clothing/head/helmet/roman/legionaire = 1, /obj/item/clothing/under/roman = 1, /obj/item/clothing/shoes/roman = 1, /obj/item/shield/riot/roman = 1) + /obj/item/clothing/head/rabbitears =1, + /obj/item/clothing/head/sombrero = 1, + /obj/item/clothing/suit/poncho = 1, + /obj/item/clothing/suit/poncho/green = 1, + /obj/item/clothing/suit/poncho/red = 1, + /obj/item/clothing/accessory/blue = 1, + /obj/item/clothing/accessory/red = 1, + /obj/item/clothing/accessory/black = 1, + /obj/item/clothing/accessory/horrible = 1, + /obj/item/clothing/under/maid = 1, + /obj/item/clothing/under/janimaid = 1, + /obj/item/clothing/under/jester = 1, + /obj/item/clothing/head/jester = 1, + /obj/item/clothing/under/pants/camo = 1, + /obj/item/clothing/mask/bandana = 1, + /obj/item/clothing/mask/bandana/black = 1, + /obj/item/clothing/shoes/singery = 1, + /obj/item/clothing/under/singery = 1, + /obj/item/clothing/shoes/singerb = 1, + /obj/item/clothing/under/singerb = 1, + /obj/item/clothing/suit/hooded/carp_costume = 1, + /obj/item/clothing/suit/hooded/bee_costume = 1, + /obj/item/clothing/suit/snowman = 1, + /obj/item/clothing/head/snowman = 1, + /obj/item/clothing/head/cueball = 1, + /obj/item/clothing/under/scratch = 1, + /obj/item/clothing/under/victdress = 1, + /obj/item/clothing/under/victdress/red = 1, + /obj/item/clothing/suit/victcoat = 1, + /obj/item/clothing/suit/victcoat/red = 1, + /obj/item/clothing/under/victsuit = 1, + /obj/item/clothing/under/victsuit/redblk = 1, + /obj/item/clothing/under/victsuit/red = 1, + /obj/item/clothing/suit/tailcoat = 1, + /obj/item/clothing/suit/draculacoat = 1, + /obj/item/clothing/head/zepelli = 1, + /obj/item/clothing/under/redhawaiianshirt = 1, + /obj/item/clothing/under/pinkhawaiianshirt = 1, + /obj/item/clothing/under/bluehawaiianshirt = 1, + /obj/item/clothing/under/orangehawaiianshirt = 1) + contraband = list(/obj/item/clothing/suit/judgerobe = 1, + /obj/item/clothing/head/powdered_wig = 1, + /obj/item/gun/magic/wand = 1, + /obj/item/clothing/mask/balaclava=1, + /obj/item/clothing/mask/horsehead = 2) + premium = list(/obj/item/clothing/suit/hgpirate = 1, + /obj/item/clothing/head/hgpiratecap = 1, + /obj/item/clothing/head/helmet/roman/fake = 1, + /obj/item/clothing/head/helmet/roman/legionaire/fake = 1, + /obj/item/clothing/under/roman = 1, + /obj/item/clothing/shoes/roman = 1, + /obj/item/shield/riot/roman/fake = 1) refill_canister = /obj/item/vending_refill/autodrobe /obj/machinery/vending/autodrobe/Initialize(mapload) diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index d6dbf1dddbb..f7a0962269d 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -90,6 +90,10 @@ /obj/effect/temp_visual/dir_setting/wraith/out icon_state = "phase_shift" +/obj/effect/temp_visual/dir_setting/tailsweep + icon_state = "tailsweep" + duration = 4 + /obj/effect/temp_visual/wizard name = "water" icon = 'icons/mob/mob.dmi' diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index 376c8cf38d2..d7038fcd752 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -231,7 +231,8 @@ create_with_tank = TRUE /obj/item/flamethrower/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(ptank && damage && attack_type == PROJECTILE_ATTACK && prob(15)) + var/obj/item/projectile/P = hitby + if(damage && attack_type == PROJECTILE_ATTACK && P.damage_type != STAMINA && prob(15)) owner.visible_message("[attack_text] hits the fueltank on [owner]'s [src], rupturing it! What a shot!") var/turf/target_turf = get_turf(owner) log_game("A projectile ([hitby]) detonated a flamethrower tank held by [key_name(owner)] at [COORD(target_turf)]") diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 5c1c7587d8c..10bbb7c7cb5 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -114,7 +114,8 @@ prime() /obj/item/grenade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(damage && attack_type == PROJECTILE_ATTACK && prob(15)) + var/obj/item/projectile/P = hitby + if(damage && attack_type == PROJECTILE_ATTACK && P.damage_type != STAMINA && prob(15)) owner.visible_message("[attack_text] hits [owner]'s [src], setting it off! What a shot!") var/turf/T = get_turf(src) log_game("A projectile ([hitby]) detonated a grenade held by [key_name(owner)] at [COORD(T)]") @@ -566,7 +567,7 @@ update_icon() /obj/item/grenade/chem_grenade/saringas - payload_name = "saringas" + payload_name = "sarin gas" desc = "Contains sarin gas; extremely deadly and fast acting; use with extreme caution." stage = READY diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index 2cbf80a6388..eebc8a0e512 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -39,6 +39,12 @@ desc = "Bears an inscription on the inside: \"Romanes venio domus\"." icon_state = "roman_shield" item_state = "roman_shield" + materials = list(MAT_METAL=8500) + +/obj/item/shield/riot/roman/fake + desc = "Bears an inscription on the inside: \"Romanes venio domus\". It appears to be a bit flimsy." + block_chance = 0 + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) /obj/item/shield/riot/buckler name = "wooden buckler" diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index ddae7d87f85..0fefed4323a 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -389,6 +389,7 @@ ..() new /obj/item/ammo_box/magazine/smgm45(src) new /obj/item/ammo_box/magazine/smgm45(src) + new /obj/item/ammo_box/magazine/smgm45(src) new /obj/item/gun/projectile/automatic/c20r(src) new /obj/item/suppressor/specialoffer(src) @@ -403,13 +404,13 @@ new /obj/item/clothing/glasses/chameleon/thermal(src) /obj/item/storage/backpack/duffel/syndie/med/medicalbundle - desc = "A large duffel bag containing a tactical medkit, a Donksoft machine gun and a big jumbo box of riot darts." + desc = "A large duffel bag containing a tactical medkit, a medical beam gun and a pair of syndicate magboots." /obj/item/storage/backpack/duffel/syndie/med/medicalbundle/New() ..() new /obj/item/storage/firstaid/tactical(src) - new /obj/item/gun/projectile/automatic/l6_saw/toy(src) - new /obj/item/ammo_box/foambox/riot(src) + new /obj/item/clothing/shoes/magboots/syndie(src) + new /obj/item/gun/medbeam(src) /obj/item/storage/backpack/duffel/syndie/c4/New() ..() diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 8ddf93302d5..579f2fddf0b 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -332,6 +332,7 @@ new /obj/item/grenade/gluon(src) new /obj/item/grenade/gluon(src) new /obj/item/grenade/chem_grenade/facid(src) //1 + new /obj/item/grenade/chem_grenade/saringas(src) //1 new /obj/item/grenade/gas/plasma(src) //2 new /obj/item/grenade/gas/plasma(src) new /obj/item/grenade/frag(src) //10 diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index 1be32df4d2a..6d81cdd2dac 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -175,7 +175,7 @@ if(ishuman(L)) var/mob/living/carbon/human/H = L - if(H.check_shields(src, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that + if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that playsound(L, 'sound/weapons/genhit.ogg', 50, 1) return diff --git a/code/game/turfs/simulated/floor/indestructible.dm b/code/game/turfs/simulated/floor/indestructible.dm index 3217a4c7996..adb41ac335e 100644 --- a/code/game/turfs/simulated/floor/indestructible.dm +++ b/code/game/turfs/simulated/floor/indestructible.dm @@ -74,15 +74,16 @@ temperature = T20C /turf/simulated/floor/indestructible/hierophant - icon_state = "hierophant1" + name = "floor" + icon = 'icons/turf/floors/hierophant_floor.dmi' + icon_state = "floor" oxygen = 14 nitrogen = 23 temperature = 300 planetary_atmos = TRUE - desc = "A floor with a square pattern. It's faintly cool to the touch." + smooth = SMOOTH_TRUE /turf/simulated/floor/indestructible/hierophant/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) return FALSE -/turf/simulated/floor/indestructible/hierophant/two - icon_state = "hierophant2" +/turf/simulated/floor/indestructible/hierophant/two \ No newline at end of file diff --git a/code/game/turfs/simulated/walls_indestructible.dm b/code/game/turfs/simulated/walls_indestructible.dm index e25992a2555..8850fbf24fa 100644 --- a/code/game/turfs/simulated/walls_indestructible.dm +++ b/code/game/turfs/simulated/walls_indestructible.dm @@ -29,17 +29,17 @@ return /turf/simulated/wall/indestructible/attackby(obj/item/I, mob/user, params) - return + return /turf/simulated/wall/indestructible/attack_hand(mob/user) - return + return /turf/simulated/wall/indestructible/attack_hulk(mob/user, does_attack_animation = FALSE) return /turf/simulated/wall/indestructible/attack_animal(mob/living/simple_animal/M) return - + /turf/simulated/wall/indestructible/mech_melee_attack(obj/mecha/M) return @@ -66,9 +66,9 @@ /turf/simulated/wall/indestructible/hierophant name = "wall" - desc = "A wall made out of smooth, cold stone." + desc = "A wall made out of a strange metal. The squares on it pulse in a predictable pattern." icon = 'icons/turf/walls/hierophant_wall.dmi' - icon_state = "hierophant" + icon_state = "wall" smooth = SMOOTH_TRUE /turf/simulated/wall/indestructible/uranium diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index a2679fc3adc..7a9b78cfb19 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -163,12 +163,20 @@ strip_delay = 100 dog_fashion = null +/obj/item/clothing/head/helmet/roman/fake + desc = "An ancient helmet made of plastic and leather." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + /obj/item/clothing/head/helmet/roman/legionaire name = "roman legionaire helmet" desc = "An ancient helmet made of bronze and leather. Has a red crest on top of it." icon_state = "roman_c" item_state = "roman_c" +/obj/item/clothing/head/helmet/roman/legionaire/fake + desc = "An ancient helmet made of plastic and leather. Has a red crest on top of it." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + /obj/item/clothing/head/helmet/gladiator name = "gladiator helmet" desc = "Ave, Imperator, morituri te salutant." diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index af366683570..b177d7a7aaf 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -269,51 +269,59 @@ Gunshots/explosions/opening doors/less rare audio (done) px = -32 /obj/effect/hallucination/oh_yeah - var/turf/simulated/wall/wall - var/obj/effect/hallucination/simple/bubblegum/bubblegum = null + var/obj/effect/hallucination/simple/bubblegum/bubblegum var/image/fakebroken var/image/fakerune -/obj/effect/hallucination/oh_yeah/New(loc, mob/living/carbon/T) +/obj/effect/hallucination/oh_yeah/New(loc, mob/living/carbon/C) + set waitfor = FALSE ..() - target = T - for(var/turf/simulated/wall/W in range(7,target)) + target = C + var/turf/simulated/wall/wall + for(var/turf/simulated/wall/W in range(7, target)) wall = W break - if(wall) - fakebroken = image('icons/turf/floors.dmi', wall, "plating", layer = TURF_LAYER) - var/turf/landing = get_turf(target) - var/turf/landing_image_turf = get_step(landing, SOUTHWEST) //the icon is 3x3 - fakerune = image('icons/effects/96x96.dmi', landing_image_turf, "landing", layer = ABOVE_OPEN_TURF_LAYER) - fakebroken.override = TRUE - if(target.client) - target.client.images |= fakebroken - target.client.images |= fakerune - target.playsound_local(wall,'sound/effects/meteorimpact.ogg', 150, 1) - bubblegum = new(wall, target) - sleep(10) //ominous wait - var/charged = FALSE //only get hit once - while(get_turf(bubblegum) != landing && target) - bubblegum.forceMove(get_step_towards(bubblegum, landing)) - bubblegum.setDir(get_dir(bubblegum, landing)) - target.playsound_local(get_turf(bubblegum), 'sound/effects/meteorimpact.ogg', 150, 1) - shake_camera(target, 2, 1) - if(bubblegum.Adjacent(target) && !charged) - charged = TRUE - target.Weaken(4) - target.adjustStaminaLoss(40) - step_away(target, bubblegum) - shake_camera(target, 4, 3) - target.visible_message("[target] jumps backwards, falling on the ground!","[bubblegum] slams into you!") - sleep(2) - sleep(30) - qdel(bubblegum) + if(!wall) + qdel(src) + return + + fakebroken = image('icons/turf/floors.dmi', wall, "plating", layer = TURF_LAYER) + var/turf/landing = get_turf(target) + var/turf/landing_image_turf = get_step(landing, SOUTHWEST) //the icon is 3x3 + fakerune = image('icons/effects/96x96.dmi', landing_image_turf, "landing", layer = ABOVE_OPEN_TURF_LAYER) + fakebroken.override = TRUE + if(target.client) + target.client.images |= fakebroken + target.client.images |= fakerune + target.playsound_local(wall,'sound/effects/meteorimpact.ogg', 150, 1) + bubblegum = new(wall, target) + addtimer(CALLBACK(src, .proc/bubble_attack, landing), 10) + +/obj/effect/hallucination/oh_yeah/proc/bubble_attack(turf/landing) + var/charged = FALSE //only get hit once + while(get_turf(bubblegum) != landing && target && target.stat != DEAD) + bubblegum.forceMove(get_step_towards(bubblegum, landing)) + bubblegum.setDir(get_dir(bubblegum, landing)) + target.playsound_local(get_turf(bubblegum), 'sound/effects/meteorimpact.ogg', 150, 1) + shake_camera(target, 2, 1) + if(bubblegum.Adjacent(target) && !charged) + charged = TRUE + target.Weaken(4) + target.adjustStaminaLoss(40) + step_away(target, bubblegum) + shake_camera(target, 4, 3) + target.visible_message("[target] jumps backwards, falling on the ground!", "[bubblegum] slams into you!") + sleep(2) + sleep(30) qdel(src) /obj/effect/hallucination/oh_yeah/Destroy() if(target.client) target.client.images.Remove(fakebroken) target.client.images.Remove(fakerune) + QDEL_NULL(fakebroken) + QDEL_NULL(fakerune) + QDEL_NULL(bubblegum) return ..() /obj/effect/hallucination/singularity_scare diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm index 41c5ff0bdf3..d85b882e7db 100644 --- a/code/modules/food_and_drinks/drinks/drinks.dm +++ b/code/modules/food_and_drinks/drinks/drinks.dm @@ -334,14 +334,6 @@ icon_state = "britcup" volume = 30 -/obj/item/reagent_containers/food/drinks/mushroom_bowl - name = "mushroom bowl" - desc = "A bowl made out of mushrooms. Not food, though it might have contained some at some point." - icon = 'icons/obj/lavaland/ash_flora.dmi' - icon_state = "mushroom_bowl" - w_class = WEIGHT_CLASS_SMALL - - /obj/item/reagent_containers/food/drinks/bag name = "drink bag" desc = "Normally put in wine boxes, or down pants at stadium events." diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm index 10e135f5fe6..b486aa629bb 100644 --- a/code/modules/hydroponics/gene_modder.dm +++ b/code/modules/hydroponics/gene_modder.dm @@ -374,6 +374,7 @@ seed.genes += disk.gene.Copy() if(istype(disk.gene, /datum/plant_gene/reagent)) seed.reagents_from_genes() + disk.gene.apply_vars(seed) repaint_seed() update_genes() diff --git a/code/modules/hydroponics/grown/beans.dm b/code/modules/hydroponics/grown/beans.dm index 206d0984c3d..8d9702f1343 100644 --- a/code/modules/hydroponics/grown/beans.dm +++ b/code/modules/hydroponics/grown/beans.dm @@ -15,7 +15,7 @@ icon_dead = "soybean-dead" genes = list(/datum/plant_gene/trait/repeated_harvest) mutatelist = list(/obj/item/seeds/soya/koi) - reagents_add = list("soybeanoil" = 0.2, "vitamin" = 0.04, "plantmatter" = 0.05) + reagents_add = list("vitamin" = 0.04, "plantmatter" = 0.05, "soybeanoil" = 0.03) /obj/item/reagent_containers/food/snacks/grown/soybeans seed = /obj/item/seeds/soya @@ -25,7 +25,7 @@ icon_state = "soybeans" filling_color = "#F0E68C" bitesize_mod = 2 - tastes = list("soybean" = 1) + tastes = list("soy" = 1) wine_power = 0.2 // Koibean diff --git a/code/modules/hydroponics/grown/berries.dm b/code/modules/hydroponics/grown/berries.dm index d4161b0a87e..9682b5ba19f 100644 --- a/code/modules/hydroponics/grown/berries.dm +++ b/code/modules/hydroponics/grown/berries.dm @@ -46,8 +46,8 @@ desc = "Taste so good, you could die!" icon_state = "poisonberrypile" filling_color = "#C71585" - distill_reagent = null tastes = list("poison-berry" = 1) + distill_reagent = null wine_power = 0.35 // Death Berries @@ -70,8 +70,8 @@ desc = "Taste so good, you could die!" icon_state = "deathberrypile" filling_color = "#708090" - distill_reagent = null tastes = list("death-berry" = 1) + distill_reagent = null wine_power = 0.5 // Glow Berries @@ -130,8 +130,8 @@ gender = PLURAL filling_color = "#FF0000" bitesize_mod = 2 - wine_power = 0.3 tastes = list("cherry" = 1) + wine_power = 0.3 // Blue Cherries /obj/item/seeds/cherry/blue @@ -152,8 +152,8 @@ icon_state = "bluecherry" filling_color = "#6495ED" bitesize_mod = 2 - wine_power = 0.5 tastes = list("blue cherry" = 1) + wine_power = 0.5 // Grapes /obj/item/seeds/grape @@ -184,8 +184,8 @@ dried_type = /obj/item/reagent_containers/food/snacks/no_raisin filling_color = "#FF1493" bitesize_mod = 2 - distill_reagent = "wine" tastes = list("grapes" = 1) + distill_reagent = "wine" // Green Grapes /obj/item/seeds/grape/green diff --git a/code/modules/hydroponics/grown/chili.dm b/code/modules/hydroponics/grown/chili.dm index 1a2b5142e37..3908262a6a2 100644 --- a/code/modules/hydroponics/grown/chili.dm +++ b/code/modules/hydroponics/grown/chili.dm @@ -51,7 +51,7 @@ filling_color = "#0000CD" bitesize_mod = 2 origin_tech = "biotech=4" - tastes = list("chilli" = 1) + tastes = list("chilly" = 1) wine_power = 0.3 // Ghost Chili diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm index 38e4104c4a7..6e937f45c2a 100644 --- a/code/modules/hydroponics/grown/citrus.dm +++ b/code/modules/hydroponics/grown/citrus.dm @@ -31,7 +31,6 @@ icon_state = "lime" filling_color = "#00FF00" tastes = list("lime" = 1) - distill_reagent = "triple_sec" // Orange /obj/item/seeds/orange @@ -59,6 +58,7 @@ icon_state = "orange" tastes = list("orange" = 1) filling_color = "#FFA500" + distill_reagent = "triple_sec" // Lemon /obj/item/seeds/lemon diff --git a/code/modules/hydroponics/grown/corn.dm b/code/modules/hydroponics/grown/corn.dm index cf7c185c147..ac7946b3864 100644 --- a/code/modules/hydroponics/grown/corn.dm +++ b/code/modules/hydroponics/grown/corn.dm @@ -62,7 +62,7 @@ /obj/item/grown/snapcorn seed = /obj/item/seeds/corn/snapcorn name = "snap corn" - desc = "A cob with snap pops" + desc = "A cob with snap pops." icon_state = "snapcorn" item_state = "corncob" w_class = WEIGHT_CLASS_TINY diff --git a/code/modules/hydroponics/grown/eggplant.dm b/code/modules/hydroponics/grown/eggplant.dm index 2ed28f76226..c1fad32bed8 100644 --- a/code/modules/hydroponics/grown/eggplant.dm +++ b/code/modules/hydroponics/grown/eggplant.dm @@ -27,9 +27,11 @@ // Egg-Plant /obj/item/seeds/eggplant/eggy + name = "pack of egg-plant seeds" desc = "These seeds grow to produce berries that look a lot like eggs." icon_state = "seed-eggy" species = "eggy" + plantname = "Egg-Plants" product = /obj/item/reagent_containers/food/snacks/grown/shell/eggy lifespan = 75 production = 12 diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm index d662a77e08a..dea72093b15 100644 --- a/code/modules/hydroponics/grown/flowers.dm +++ b/code/modules/hydroponics/grown/flowers.dm @@ -138,6 +138,8 @@ icon_state = "seed-moonflower" species = "moonflower" plantname = "Moonflowers" + icon_grow = "moonflower-grow" + icon_dead = "sunflower-dead" product = /obj/item/reagent_containers/food/snacks/grown/moonflower mutatelist = list() reagents_add = list("moonshine" = 0.2, "vitamin" = 0.02, "plantmatter" = 0.02) @@ -152,7 +154,7 @@ filling_color = "#E6E6FA" bitesize_mod = 2 tastes = list("moonflower" = 1) - distill_reagent = "absinthe" + distill_reagent = "absinthe" //It's made from flowers. // Novaflower /obj/item/seeds/sunflower/novaflower @@ -161,6 +163,8 @@ icon_state = "seed-novaflower" species = "novaflower" plantname = "Novaflowers" + icon_grow = "novaflower-grow" + icon_dead = "sunflower-dead" product = /obj/item/grown/novaflower mutatelist = list() reagents_add = list("condensedcapsaicin" = 0.25, "capsaicin" = 0.3, "plantmatter" = 0) diff --git a/code/modules/hydroponics/grown/garlic.dm b/code/modules/hydroponics/grown/garlic.dm new file mode 100644 index 00000000000..eccc9dea1f2 --- /dev/null +++ b/code/modules/hydroponics/grown/garlic.dm @@ -0,0 +1,22 @@ +/obj/item/seeds/garlic + name = "pack of garlic seeds" + desc = "A packet of extremely pungent seeds." + icon_state = "seed-garlic" + species = "garlic" + plantname = "Garlic Sprouts" + product = /obj/item/reagent_containers/food/snacks/grown/garlic + yield = 6 + potency = 25 + growthstages = 3 + growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi' + reagents_add = list("garlic" = 0.15, "plantmatter" = 0.1) + +/obj/item/reagent_containers/food/snacks/grown/garlic + seed = /obj/item/seeds/garlic + name = "garlic" + desc = "Delicious, but with a potentially overwhelming odor." + icon_state = "garlic" + filling_color = "#C0C9A0" + bitesize_mod = 2 + tastes = list("garlic" = 1) + wine_power = 0.1 \ No newline at end of file diff --git a/code/modules/hydroponics/grown/grass_carpet.dm b/code/modules/hydroponics/grown/grass_carpet.dm index dee15bf75b2..832645c95cd 100644 --- a/code/modules/hydroponics/grown/grass_carpet.dm +++ b/code/modules/hydroponics/grown/grass_carpet.dm @@ -38,17 +38,8 @@ continue grassAmt += 1 + round(G.seed.potency * tile_coefficient) qdel(G) - var/obj/item/stack/tile/GT = new stacktype(user.loc) - while(grassAmt > GT.max_amount) - GT.amount = GT.max_amount - grassAmt -= GT.max_amount - GT = new stacktype(user.loc) - GT.amount = grassAmt - for(var/obj/item/stack/tile/T in user.loc) - if((T.type == stacktype) && (T.amount < T.max_amount) && (T != GT)) - T.attackby(GT, user) + new stacktype(user.drop_location(), grassAmt) qdel(src) - return // Carpet /obj/item/seeds/grass/carpet diff --git a/code/modules/hydroponics/grown/melon.dm b/code/modules/hydroponics/grown/melon.dm index 09952a2a405..61c23a294b8 100644 --- a/code/modules/hydroponics/grown/melon.dm +++ b/code/modules/hydroponics/grown/melon.dm @@ -14,6 +14,13 @@ mutatelist = list(/obj/item/seeds/watermelon/holy) reagents_add = list("water" = 0.2, "vitamin" = 0.04, "plantmatter" = 0.2) +/obj/item/seeds/watermelon/suicide_act(mob/user) + user.visible_message("[user] is swallowing [src]! It looks like [user.p_theyre()] trying to commit suicide!") + user.gib() + new product(drop_location()) + qdel(src) + return OBLITERATION + /obj/item/reagent_containers/food/snacks/grown/watermelon seed = /obj/item/seeds/watermelon name = "watermelon" diff --git a/code/modules/hydroponics/grown/misc.dm b/code/modules/hydroponics/grown/misc.dm index f2adfab0f20..171f777abd7 100644 --- a/code/modules/hydroponics/grown/misc.dm +++ b/code/modules/hydroponics/grown/misc.dm @@ -14,19 +14,19 @@ growthstages = 3 growing_icon = 'icons/obj/hydroponics/growing_flowers.dmi' genes = list(/datum/plant_gene/trait/plant_type/weed_hardy) - mutatelist = list(/obj/item/seeds/harebell) /obj/item/seeds/starthistle/harvest(mob/user) var/obj/machinery/hydroponics/parent = loc + var/seed_count = yield if(prob(getYield() * 20)) + seed_count++ var/output_loc = parent.Adjacent(user) ? user.loc : parent.loc - for(var/i in 1 to yield+1) + for(var/i in 1 to seed_count) var/obj/item/seeds/starthistle/harvestseeds = Copy() harvestseeds.forceMove(output_loc) parent.update_tray() - // Cabbage /obj/item/seeds/cabbage name = "pack of cabbage seeds" diff --git a/code/modules/hydroponics/grown/mushrooms.dm b/code/modules/hydroponics/grown/mushrooms.dm index 68331c664c9..45d7e76e6b4 100644 --- a/code/modules/hydroponics/grown/mushrooms.dm +++ b/code/modules/hydroponics/grown/mushrooms.dm @@ -175,7 +175,7 @@ can_distill = FALSE /obj/item/reagent_containers/food/snacks/grown/mushroom/walkingmushroom/attack_self(mob/user) - if(istype(user.loc, /turf/space)) + if(isspaceturf(user.loc)) return var/mob/living/simple_animal/hostile/mushroom/M = new /mob/living/simple_animal/hostile/mushroom(user.loc) M.maxHealth += round(seed.endurance / 4) @@ -277,8 +277,7 @@ desc = "This mycelium -powers- into mushrooms!" icon_state = "mycelium-glowcap" species = "glowcap" - icon_grow = "glowshroom-grow" - icon_dead = "glowshroom-dead" + icon_harvest = "glowcap-harvest" plantname = "Glowcaps" product = /obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom/glowcap genes = list(/datum/plant_gene/trait/glow/red, /datum/plant_gene/trait/cell_charge, /datum/plant_gene/trait/plant_type/fungal_metabolism) diff --git a/code/modules/hydroponics/grown/onion.dm b/code/modules/hydroponics/grown/onion.dm index 597d5c70660..c53a9487db4 100644 --- a/code/modules/hydroponics/grown/onion.dm +++ b/code/modules/hydroponics/grown/onion.dm @@ -26,7 +26,7 @@ slice_path = /obj/item/reagent_containers/food/snacks/onion_slice tastes = list("onion" = 1, "pungentness" = 1) slices_num = 2 - wine_power = 0.2 + wine_power = 0.3 wine_flavor = "pungentness" /obj/item/seeds/onion/red diff --git a/code/modules/hydroponics/grown/pineapple.dm b/code/modules/hydroponics/grown/pineapple.dm index 4a2975fae42..30c5f904951 100644 --- a/code/modules/hydroponics/grown/pineapple.dm +++ b/code/modules/hydroponics/grown/pineapple.dm @@ -1,29 +1,33 @@ +// Pineapple! /obj/item/seeds/pineapple - name = "pack of pineapple seeds" - desc = "These seeds grow into pineapple plants." - icon_state = "seed-pineapple" //NEEDED - species = "pineapple" - plantname = "Pineapple Plant" - product = /obj/item/reagent_containers/food/snacks/grown/pineapple - maturation = 6 - lifespan = 55 - endurance = 35 - growthstages = 4 - growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' - icon_grow = "pineapple-grow" //NEEDED - icon_dead = "pineapple-dead" //NEEDED - genes = list(/datum/plant_gene/trait/repeated_harvest) - reagents_add = list("water" = 0.1, "vitamin" = 0.03, "sugar" = 0.02, "plantmatter" = 0.2) + name = "pack of pineapple seeds" + desc = "Oooooooooooooh!" + icon_state = "seed-pineapple" + species = "pineapple" + plantname = "Pineapple Plant" + product = /obj/item/reagent_containers/food/snacks/grown/pineapple + lifespan = 40 + endurance = 30 + growthstages = 3 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/apple) + reagents_add = list("vitamin" = 0.02, "plantmatter" = 0.2, "water" = 0.04) /obj/item/reagent_containers/food/snacks/grown/pineapple - seed = /obj/item/seeds/pineapple - name = "pineapple" - desc = "A soft sweet interior surrounded by a spiky skin." - slice_path = /obj/item/reagent_containers/food/snacks/pineappleslice - slices_num = 4 - icon_state = "pineapple" - filling_color = "#e5b437" - w_class = WEIGHT_CLASS_NORMAL - bitesize_mod = 3 - tastes = list("pineapple" = 1) - wine_power = 0.4 \ No newline at end of file + seed = /obj/item/seeds/pineapple + name = "pineapple" + desc = "A soft sweet interior surrounded by a spiky skin." + icon_state = "pineapple" + force = 4 + throwforce = 8 + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("stung", "pined") + throw_speed = 1 + throw_range = 5 + slice_path = /obj/item/reagent_containers/food/snacks/pineappleslice + slices_num = 3 + filling_color = "#F6CB0B" + w_class = WEIGHT_CLASS_NORMAL + tastes = list("pineapple" = 1) + wine_power = 0.4 \ No newline at end of file diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm index 3aaa8d87041..d6f8cae7519 100644 --- a/code/modules/hydroponics/grown/replicapod.dm +++ b/code/modules/hydroponics/grown/replicapod.dm @@ -98,8 +98,6 @@ podman.ckey = ckey_holder podman.gender = blood_gender podman.faction |= factions - podman.faction |= "plants" - podman.faction |= "vines" //Pod grown Diona are allied with plants and vines alike. else //else, one packet of seeds. maybe two var/seed_count = 1 diff --git a/code/modules/hydroponics/grown/towercap.dm b/code/modules/hydroponics/grown/towercap.dm index 33ef72db471..156a426e202 100644 --- a/code/modules/hydroponics/grown/towercap.dm +++ b/code/modules/hydroponics/grown/towercap.dm @@ -44,15 +44,11 @@ attack_verb = list("bashed", "battered", "bludgeoned", "whacked") var/plank_type = /obj/item/stack/sheet/wood var/plank_name = "wooden planks" - var/list/accepted = list(/obj/item/reagent_containers/food/snacks/grown/tobacco, + var/static/list/accepted = typecacheof(list(/obj/item/reagent_containers/food/snacks/grown/tobacco, /obj/item/reagent_containers/food/snacks/grown/tea, /obj/item/reagent_containers/food/snacks/grown/ambrosia/vulgaris, /obj/item/reagent_containers/food/snacks/grown/ambrosia/deus, - /obj/item/reagent_containers/food/snacks/grown/wheat) - -/obj/item/grown/log/New() - ..() - accepted = typecacheof(accepted) + /obj/item/reagent_containers/food/snacks/grown/wheat)) /obj/item/grown/log/attackby(obj/item/W, mob/user, params) if(is_sharp(W)) @@ -69,7 +65,7 @@ to_chat(user, "You add the newly-formed [plank_name] to the stack. It now contains [plank.amount] [plank_name].") qdel(src) - if(is_type_in_typecache(W, accepted)) + if(CheckAccepted(W)) var/obj/item/reagent_containers/food/snacks/grown/leaf = W if(leaf.dry) user.show_message("You wrap \the [W] around the log, turning it into a torch!") @@ -84,6 +80,9 @@ else return ..() +/obj/item/grown/log/proc/CheckAccepted(obj/item/I) + return is_type_in_typecache(I, accepted) + /obj/item/grown/log/tree seed = null name = "wood log" @@ -94,10 +93,11 @@ name = "steel-cap log" desc = "It's made of metal." icon_state = "steellogs" - accepted = list() plank_type = /obj/item/stack/rods plank_name = "rods" +/obj/item/grown/log/steel/CheckAccepted(obj/item/I) + return FALSE /////////BONFIRES////////// diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 92f91ddf51a..25ae26da3ad 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -636,9 +636,10 @@ // why, just why if(S.has_reagent("napalm", 1)) - adjustHealth(-round(S.get_reagent_amount("napalm") * 6)) - adjustToxic(round(S.get_reagent_amount("napalm") * 7)) - adjustWeeds(-rand(5,9)) + if(!(myseed.resistance_flags & FIRE_PROOF)) + adjustHealth(-round(S.get_reagent_amount("napalm") * 6)) + adjustToxic(round(S.get_reagent_amount("napalm") * 7)) + adjustWeeds(-rand(5, 9)) //At least give them a small reward if they bother //Weed Spray if(S.has_reagent("atrazine", 1)) diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm index ef450f4a52f..b38cd5710a2 100644 --- a/code/modules/hydroponics/plant_genes.dm +++ b/code/modules/hydroponics/plant_genes.dm @@ -10,7 +10,8 @@ /datum/plant_gene/proc/Copy() return new type - +/datum/plant_gene/proc/apply_vars(obj/item/seeds/S) // currently used for fire resist, can prob. be further refactored + return // Core plant genes store 5 main variables: lifespan, endurance, production, yield, potency @@ -412,6 +413,17 @@ S.set_up(G.reagents, splat_location) S.start(smoke_amount) +/datum/plant_gene/trait/fire_resistance // Lavaland + name = "Fire Resistance" + +/datum/plant_gene/trait/fire_resistance/apply_vars(obj/item/seeds/S) + if(!(S.resistance_flags & FIRE_PROOF)) + S.resistance_flags |= FIRE_PROOF + +/datum/plant_gene/trait/fire_resistance/on_new(obj/item/reagent_containers/food/snacks/grown/G, newloc) + if(!(G.resistance_flags & FIRE_PROOF)) + G.resistance_flags |= FIRE_PROOF + /datum/plant_gene/trait/plant_type // Parent type name = "you shouldn't see this" trait_id = "plant_type" diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm index 11669e000fd..972ca9af868 100644 --- a/code/modules/mining/equipment/survival_pod.dm +++ b/code/modules/mining/equipment/survival_pod.dm @@ -164,7 +164,6 @@ name = "survival pod medical supply" desc = "Wall-mounted Medical Equipment dispenser. This one seems just a tiny bit smaller." req_access = list() - refill_canister = null products = list(/obj/item/reagent_containers/food/pill/patch/styptic = 5, /obj/item/reagent_containers/food/pill/patch/silver_sulf = 5, @@ -342,7 +341,7 @@ /obj/item/shield/changeling, /obj/item/lava_staff, /obj/item/katana/energy, - /obj/item/hierophant_staff, + /obj/item/hierophant_club, /obj/item/storage/toolbox/green/memetic, /obj/item/gun/projectile/automatic/l6_saw, /obj/item/gun/magic/staff/chaos, diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm index d9747a3a1f4..46df9ddfd9b 100644 --- a/code/modules/mining/lavaland/ash_flora.dm +++ b/code/modules/mining/lavaland/ash_flora.dm @@ -1,5 +1,6 @@ /obj/structure/flora/ash gender = PLURAL + layer = PROJECTILE_HIT_THRESHHOLD_LAYER //sporangiums up don't shoot icon = 'icons/obj/lavaland/ash_flora.dmi' icon_state = "l_mushroom" name = "large mushrooms" @@ -17,42 +18,41 @@ var/harvest_message_high = "You harvest and collect shavings from several mushroom caps." var/harvested = FALSE var/base_icon - var/regrowth_time_low = 4800 - var/regrowth_time_high = 8400 + var/regrowth_time_low = 8 MINUTES + var/regrowth_time_high = 16 MINUTES -/obj/structure/flora/ash/New() - ..() +/obj/structure/flora/ash/Initialize(mapload) + . = ..() base_icon = "[icon_state][rand(1, 4)]" icon_state = base_icon - if(prob(15)) - harvest(null, TRUE) -/obj/structure/flora/ash/ex_act(severity, target) - switch(severity) - if(1) - qdel(src) - if(2) - if(prob(80)) - qdel(src) - if(3) - if(prob(50)) +/obj/structure/flora/ash/ex_act(severity, target) + switch(severity) + if(1) + qdel(src) + if(2) + if(prob(80)) + qdel(src) + if(3) + if(prob(50)) qdel(src) -/obj/structure/flora/ash/proc/harvest(user, no_drop) +/obj/structure/flora/ash/proc/harvest(user) if(harvested) return 0 - if(!no_drop) - var/rand_harvested = rand(harvest_amount_low, harvest_amount_high) - if(rand_harvested) - if(user) - var/msg = harvest_message_med - if(rand_harvested == harvest_amount_low) - msg = harvest_message_low - else if(rand_harvested == harvest_amount_high) - msg = harvest_message_high - to_chat(user, "[msg]") - for(var/i in 1 to rand_harvested) - new harvest(get_turf(src)) + + var/rand_harvested = rand(harvest_amount_low, harvest_amount_high) + if(rand_harvested) + if(user) + var/msg = harvest_message_med + if(rand_harvested == harvest_amount_low) + msg = harvest_message_low + else if(rand_harvested == harvest_amount_high) + msg = harvest_message_high + to_chat(user, "[msg]") + for(var/i in 1 to rand_harvested) + new harvest(get_turf(src)) + icon_state = "[base_icon]p" name = harvested_name desc = harvested_desc @@ -120,7 +120,8 @@ icon_state = "t_mushroom" name = "numerous mushrooms" desc = "A large number of mushrooms, some of which have long, fleshy stems. They're radiating light!" - luminosity = 1 + light_range = 1.5 + light_power = 2.1 harvested_name = "tiny mushrooms" harvested_desc = "A few tiny mushrooms around larger stumps. You can already see them growing back." harvest = /obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_stem @@ -166,12 +167,12 @@ desc = "Some shavings from a tall mushroom. With enough, might serve as a bowl." icon = 'icons/obj/lavaland/ash_flora.dmi' icon_state = "mushroom_shavings" - list_reagents = list("sugar" = 3, "ethanol" = 2, "stabilizing_agent" = 3, "minttoxin" = 2) w_class = WEIGHT_CLASS_TINY + seed = /obj/item/seeds/lavaland/polypore wine_power = 0.2 -/obj/item/reagent_containers/food/snacks/grown/ash_flora/New() - ..() +/obj/item/reagent_containers/food/snacks/grown/ash_flora/Initialize(mapload) + . = ..() pixel_x = rand(-4, 4) pixel_y = rand(-4, 4) @@ -180,39 +181,105 @@ /obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_leaf name = "mushroom leaf" desc = "A leaf, from a mushroom." - list_reagents = list("nutriment" = 3, "vitfro" = 2, "nicotine" = 2) icon_state = "mushroom_leaf" + seed = /obj/item/seeds/lavaland/porcini wine_power = 0.4 /obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_cap name = "mushroom cap" desc = "The cap of a large mushroom." - list_reagents = list("lsd" = 2, "entpoly" = 4, "psilocybin" = 2) icon_state = "mushroom_cap" + seed = /obj/item/seeds/lavaland/inocybe wine_power = 0.7 - /obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_stem name = "mushroom stem" desc = "A long mushroom stem. It's slightly glowing." - list_reagents = list("tinlux" = 2, "vitamin" = 1, "space_drugs" = 1) icon_state = "mushroom_stem" - luminosity = 1 + seed = /obj/item/seeds/lavaland/ember wine_power = 0.6 /obj/item/reagent_containers/food/snacks/grown/ash_flora/cactus_fruit name = "cactus fruit" - list_reagents = list("vitamin" = 2, "nutriment" = 2, "vitfro" = 4) desc = "A cactus fruit covered in a thick, reddish skin. And some ash." icon_state = "cactus_fruit" + seed = /obj/item/seeds/lavaland/cactus wine_power = 0.5 -/obj/item/mushroom_bowl - name = "mushroom bowl" - desc = "A bowl made out of mushrooms. Not food, though it might have contained some at some point." - icon = 'icons/obj/lavaland/ash_flora.dmi' - icon_state = "mushroom_bowl" - w_class = WEIGHT_CLASS_SMALL +//SEEDS + +/obj/item/seeds/lavaland + name = "lavaland seeds" + desc = "You should never see this." + lifespan = 50 + endurance = 25 + maturation = 7 + production = 4 + yield = 4 + potency = 15 + growthstages = 3 + rarity = 20 + reagents_add = list("nutriment" = 0.1) + resistance_flags = FIRE_PROOF + +/obj/item/seeds/lavaland/cactus + name = "pack of fruiting cactus seeds" + desc = "These seeds grow into fruiting cacti." + icon_state = "seed-cactus" + species = "cactus" + plantname = "Fruiting Cactus" + product = /obj/item/reagent_containers/food/snacks/grown/ash_flora/cactus_fruit + genes = list(/datum/plant_gene/trait/fire_resistance) + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + growthstages = 2 + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.04, "vitfro" = 0.08) + +/obj/item/seeds/lavaland/polypore + name = "pack of polypore mycelium" + desc = "This mycelium grows into bracket mushrooms, also known as polypores. Woody and firm, shaft miners often use them for makeshift crafts." + icon_state = "mycelium-polypore" + species = "polypore" + plantname = "Polypore Mushrooms" + product = /obj/item/reagent_containers/food/snacks/grown/ash_flora/shavings + genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism, /datum/plant_gene/trait/fire_resistance) + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + reagents_add = list("sugar" = 0.06, "ethanol" = 0.04, "stabilizing_agent" = 0.06, "minttoxin" = 0.02) + +/obj/item/seeds/lavaland/porcini + name = "pack of porcini mycelium" + desc = "This mycelium grows into Boletus edulus, also known as porcini. Native to the late Earth, but discovered on Lavaland. Has culinary, medicinal and relaxant effects." + icon_state = "mycelium-porcini" + species = "porcini" + plantname = "Porcini Mushrooms" + product = /obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_leaf + genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism, /datum/plant_gene/trait/fire_resistance) + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + reagents_add = list("nutriment" = 0.06, "vitfro" = 0.04, "nicotine" = 0.04) + + +/obj/item/seeds/lavaland/inocybe + name = "pack of inocybe mycelium" + desc = "This mycelium grows into an inocybe mushroom, a species of Lavaland origin with hallucinatory and toxic effects." + icon_state = "mycelium-inocybe" + species = "inocybe" + plantname = "Inocybe Mushrooms" + product = /obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_cap + genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism, /datum/plant_gene/trait/fire_resistance) + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + reagents_add = list("lsd" = 0.04, "entpoly" = 0.08, "psilocybin" = 0.04) + +/obj/item/seeds/lavaland/ember + name = "pack of embershroom mycelium" + desc = "This mycelium grows into embershrooms, a species of bioluminescent mushrooms native to Lavaland." + icon_state = "mycelium-ember" + species = "ember" + plantname = "Embershroom Mushrooms" + product = /obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_stem + genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism, /datum/plant_gene/trait/glow, /datum/plant_gene/trait/fire_resistance) + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + reagents_add = list("tinlux" = 0.04, "vitamin" = 0.02, "space_drugs" = 0.02) + +//CRAFTING //what you can craft with these things /datum/crafting_recipe/mushroom_bowl @@ -221,3 +288,10 @@ reqs = list(/obj/item/reagent_containers/food/snacks/grown/ash_flora/shavings = 5) time = 30 category = CAT_PRIMAL + +/obj/item/reagent_containers/food/drinks/mushroom_bowl + name = "mushroom bowl" + desc = "A bowl made out of mushrooms. Not food, though it might have contained some at some point." + icon = 'icons/obj/lavaland/ash_flora.dmi' + icon_state = "mushroom_bowl" + w_class = WEIGHT_CLASS_SMALL \ No newline at end of file diff --git a/code/modules/mining/lavaland/loot/hierophant_loot.dm b/code/modules/mining/lavaland/loot/hierophant_loot.dm index 23e894746d2..d7f47baed1d 100644 --- a/code/modules/mining/lavaland/loot/hierophant_loot.dm +++ b/code/modules/mining/lavaland/loot/hierophant_loot.dm @@ -1,115 +1,190 @@ -/obj/item/hierophant_staff - name = "Hierophant's staff" - desc = "A large club with intense magic power infused into it." - icon_state = "hierophant_staff" - item_state = "hierophant_staff" - icon = 'icons/obj/guns/magic.dmi' +#define HIEROPHANT_CLUB_CARDINAL_DAMAGE 30 + +/obj/item/hierophant_club + name = "hierophant club" + desc = "The strange technology of this large club allows various nigh-magical feats. It used to beat you, but now you can set the beat." + icon_state = "hierophant_club_ready_beacon" + item_state = "hierophant_club_ready_beacon" + icon = 'icons/obj/lavaland/artefacts.dmi' + lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' + righthand_file = 'icons/mob/inhands/64x64_righthand.dmi' + inhand_x_dimension = 64 + inhand_y_dimension = 64 slot_flags = SLOT_BACK w_class = WEIGHT_CLASS_BULKY - force = 20 - hitsound = "swing_hit" - //hitsound = 'sound/weapons/sonic_jackhammer.ogg' + force = 15 + attack_verb = list("clubbed", "beat", "pummeled") + hitsound = 'sound/weapons/sonic_jackhammer.ogg' actions_types = list(/datum/action/item_action/vortex_recall, /datum/action/item_action/toggle_unfriendly_fire) var/cooldown_time = 20 //how long the cooldown between non-melee ranged attacks is - var/chaser_cooldown = 101 //how long the cooldown between firing chasers at mobs is + var/chaser_cooldown = 81 //how long the cooldown between firing chasers at mobs is var/chaser_timer = 0 //what our current chaser cooldown is + var/chaser_speed = 0.8 //how fast our chasers are var/timer = 0 //what our current cooldown is - var/blast_range = 3 //how long the cardinal blast's walls are - var/obj/effect/hierophant/rune //the associated rune we teleport to + var/blast_range = 13 //how long the cardinal blast's walls are + var/obj/effect/hierophant/beacon //the associated beacon we teleport to var/teleporting = FALSE //if we ARE teleporting var/friendly_fire_check = FALSE //if the blasts we make will consider our faction against the faction of hit targets -/obj/item/hierophant_staff/afterattack(atom/target, mob/user, proximity_flag, click_parameters) +/obj/item/hierophant_club/examine(mob/user) + ..() + to_chat(user, "The[beacon ? " beacon is not currently":"re is a beacon"] attached.") + +/obj/item/hierophant_club/suicide_act(mob/living/user) + atom_say("Xverwpsgexmrk...") + user.visible_message("[user] holds [src] into the air! It looks like [user.p_theyre()] trying to commit suicide!") + new/obj/effect/temp_visual/hierophant/telegraph(get_turf(user)) + playsound(user,'sound/machines/airlock_open.ogg', 75, TRUE) + user.visible_message("[user] fades out, leaving [user.p_their()] belongings behind!") + for(var/obj/item/I in user) + if(I != src) + user.unEquip(I) + for(var/turf/T in RANGE_TURFS(1, user)) + var/obj/effect/temp_visual/hierophant/blast/B = new(T, user, TRUE) + B.damage = 0 + user.unEquip(src) //Drop us last, so it goes on top of their stuff + qdel(user) + return OBLITERATION + + +/obj/item/hierophant_club/afterattack(atom/target, mob/user, proximity_flag, click_parameters) ..() var/turf/T = get_turf(target) if(!T || timer > world.time) return + calculate_anger_mod(user) timer = world.time + CLICK_CD_MELEE //by default, melee attacks only cause melee blasts, and have an accordingly short cooldown if(proximity_flag) - spawn(0) - aoe_burst(T, user) + INVOKE_ASYNC(src, .proc/aoe_burst, T, user) add_attack_logs(user, target, "Fired 3x3 blast at [src]") else if(ismineralturf(target) && get_dist(user, target) < 6) //target is minerals, we can hit it(even if we can't see it) - spawn(0) - cardinal_blasts(T, user) + INVOKE_ASYNC(src, .proc/cardinal_blasts, T, user) timer = world.time + cooldown_time else if(target in view(5, get_turf(user))) //if the target is in view, hit it timer = world.time + cooldown_time if(isliving(target) && chaser_timer <= world.time) //living and chasers off cooldown? fire one! chaser_timer = world.time + chaser_cooldown - new /obj/effect/temp_visual/hierophant/chaser(get_turf(user), user, target, 1.5, friendly_fire_check) + var/obj/effect/temp_visual/hierophant/chaser/C = new(get_turf(user), user, target, chaser_speed, friendly_fire_check) + C.damage = 30 + C.monster_damage_boost = FALSE add_attack_logs(user, target, "Fired a chaser at [src]") else - spawn(0) - cardinal_blasts(T, user) //otherwise, just do cardinal blast + INVOKE_ASYNC(src, .proc/cardinal_blasts, T, user) //otherwise, just do cardinal blast add_attack_logs(user, target, "Fired cardinal blast at [src]") else - to_chat(user, "That target is out of range!") //too far away + to_chat(user, "That target is out of range!" ) + timer = world.time + INVOKE_ASYNC(src, .proc/prepare_icon_update) -/obj/item/hierophant_staff/ui_action_click(mob/user, actiontype) +/obj/item/hierophant_club/proc/calculate_anger_mod(mob/user) //we get stronger as the user loses health + chaser_cooldown = initial(chaser_cooldown) + cooldown_time = initial(cooldown_time) + chaser_speed = initial(chaser_speed) + blast_range = initial(blast_range) + if(isliving(user)) + var/mob/living/L = user + var/health_percent = L.health / L.maxHealth + chaser_cooldown += round(health_percent * 20) //two tenths of a second for each missing 10% of health + cooldown_time += round(health_percent * 10) //one tenth of a second for each missing 10% of health + chaser_speed = max(chaser_speed + health_percent, 0.5) //one tenth of a second faster for each missing 10% of health + blast_range -= round(health_percent * 10) //one additional range for each missing 10% of health + +/obj/item/hierophant_club/update_icon() + icon_state = "hierophant_club[timer <= world.time ? "_ready":""][(beacon && !QDELETED(beacon)) ? "":"_beacon"]" + item_state = icon_state + if(ismob(loc)) + var/mob/M = loc + M.update_inv_l_hand() + M.update_inv_r_hand() + M.update_inv_back() + +/obj/item/hierophant_club/proc/prepare_icon_update() + update_icon() + sleep(timer - world.time) + update_icon() + +/obj/item/hierophant_club/ui_action_click(mob/user, actiontype) if(actiontype == /datum/action/item_action/toggle_unfriendly_fire) //toggle friendly fire... friendly_fire_check = !friendly_fire_check to_chat(user, "You toggle friendly fire [friendly_fire_check ? "off":"on"]!") return - if(user.is_in_active_hand(src) && user.is_in_inactive_hand(src)) //you need to hold the staff to teleport - to_chat(user, "You need to hold the staff in your hands to [rune ? "teleport with it" : "create a rune"]!") + if(timer > world.time) return - if(!rune) + if(user.is_in_active_hand(src) && user.is_in_inactive_hand(src)) //you need to hold the staff to teleport + to_chat(user, "You need to hold the club in your hands to [beacon ? "teleport with it":"detach the beacon"]!") + return + if(!beacon || QDELETED(beacon)) if(isturf(user.loc)) - user.visible_message("[user] holds [src] carefully in front of [user.p_them()], moving it in a strange pattern...", \ - "You start creating a hierophant rune to teleport to...") + user.visible_message("[user] starts fiddling with [src]'s pommel...", \ + "You start detaching the hierophant beacon...") timer = world.time + 51 - if(do_after(user, 50, target = user)) + INVOKE_ASYNC(src, .proc/prepare_icon_update) + if(do_after(user, 50, target = user) && !beacon) var/turf/T = get_turf(user) - playsound(T,'sound/magic/blind.ogg', 200, 1, -4) + playsound(T,'sound/magic/blind.ogg', 200, TRUE, -4) new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, user) - var/obj/effect/hierophant/H = new/obj/effect/hierophant(T) - rune = H + beacon = new/obj/effect/hierophant(T) user.update_action_buttons_icon() - user.visible_message("[user] creates a strange rune beneath [user.p_them()]!", \ - "You create a hierophant rune, which you can teleport yourself and any allies to at any time!\n\ - You can remove the rune to place a new one by striking it with the staff.") + user.visible_message("[user] places a strange machine beneath [user.p_their()] feet!", \ + "You detach the hierophant beacon, allowing you to teleport yourself and any allies to it at any time!\n\ + You can remove the beacon to place it again by striking it with the club.") else timer = world.time + INVOKE_ASYNC(src, .proc/prepare_icon_update) else - to_chat(user, "You need to be on solid ground to produce a rune!") + to_chat(user, "You need to be on solid ground to detach the beacon!") return - if(get_dist(user, rune) <= 2) //rune too close abort - to_chat(user, "You are too close to the rune to teleport to it!") + if(get_dist(user, beacon) <= 2) //beacon too close abort + to_chat(user, "You are too close to the beacon to teleport to it!") return - if(is_blocked_turf(get_turf(rune))) - to_chat(user, "The rune is blocked by something, preventing teleportation!") + if(is_blocked_turf(get_turf(beacon), TRUE)) + to_chat(user, "The beacon is blocked by something, preventing teleportation!") + return + if(!isturf(user.loc)) + to_chat(user, "You don't have enough space to teleport from here!") return teleporting = TRUE //start channel user.update_action_buttons_icon() user.visible_message("[user] starts to glow faintly...") timer = world.time + 50 - if(do_after(user, 40, target = user) && rune) - var/turf/T = get_turf(rune) + INVOKE_ASYNC(src, .proc/prepare_icon_update) + beacon.icon_state = "hierophant_tele_on" + var/obj/effect/temp_visual/hierophant/telegraph/edge/TE1 = new /obj/effect/temp_visual/hierophant/telegraph/edge(user.loc) + var/obj/effect/temp_visual/hierophant/telegraph/edge/TE2 = new /obj/effect/temp_visual/hierophant/telegraph/edge(beacon.loc) + if(do_after(user, 40, target = user) && user && beacon) + var/turf/T = get_turf(beacon) var/turf/source = get_turf(user) - if(is_blocked_turf(T)) + if(is_blocked_turf(T, TRUE)) teleporting = FALSE - to_chat(user, "The rune is blocked by something, preventing teleportation!") + to_chat(user, "The beacon is blocked by something, preventing teleportation!") user.update_action_buttons_icon() + timer = world.time + INVOKE_ASYNC(src, .proc/prepare_icon_update) + beacon.icon_state = "hierophant_tele_off" return new /obj/effect/temp_visual/hierophant/telegraph(T, user) new /obj/effect/temp_visual/hierophant/telegraph(source, user) - playsound(T,'sound/magic/blink.ogg', 200, 1) - //playsound(T,'sound/magic/wand_teleport.ogg', 200, 1) - playsound(source,'sound/magic/blink.ogg', 200, 1) - //playsound(source,'sound/machines/AirlockOpen.ogg', 200, 1) - if(!do_after(user, 3, target = user) || !rune) //no walking away shitlord + playsound(T,'sound/magic/wand_teleport.ogg', 200, TRUE) + playsound(source,'sound/machines/airlock_open.ogg', 200, TRUE) + if(!do_after(user, 3, target = user) || !user || !beacon || QDELETED(beacon)) //no walking away shitlord teleporting = FALSE if(user) user.update_action_buttons_icon() + timer = world.time + INVOKE_ASYNC(src, .proc/prepare_icon_update) + if(beacon) + beacon.icon_state = "hierophant_tele_off" return - if(is_blocked_turf(T)) + if(is_blocked_turf(T, TRUE)) teleporting = FALSE - to_chat(user, "The rune is blocked by something, preventing teleportation!") + to_chat(user, "The beacon is blocked by something, preventing teleportation!") user.update_action_buttons_icon() + timer = world.time + INVOKE_ASYNC(src, .proc/prepare_icon_update) + beacon.icon_state = "hierophant_tele_off" return - add_attack_logs(user, rune, "Teleported self from ([source.x],[source.y],[source.z]) to ([T.x],[T.y],[T.z])") + add_attack_logs(user, beacon, "Teleported self from ([AREACOORD(source)]) to ([AREACOORD(beacon)])") new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, user) new /obj/effect/temp_visual/hierophant/telegraph/teleport(source, user) for(var/t in RANGE_TURFS(1, T)) @@ -119,18 +194,24 @@ var/obj/effect/temp_visual/hierophant/blast/B = new /obj/effect/temp_visual/hierophant/blast(t, user, TRUE) //but absolutely will hurt enemies B.damage = 30 for(var/mob/living/L in range(1, source)) - spawn(0) - teleport_mob(source, L, T, user) //regardless, take all mobs near us along + INVOKE_ASYNC(src, .proc/teleport_mob, source, L, T, user) //regardless, take all mobs near us along sleep(6) //at this point the blasts detonate + if(beacon) + beacon.icon_state = "hierophant_tele_off" else + qdel(TE1) + qdel(TE2) timer = world.time + INVOKE_ASYNC(src, .proc/prepare_icon_update) + if(beacon) + beacon.icon_state = "hierophant_tele_off" teleporting = FALSE if(user) user.update_action_buttons_icon() -/obj/item/hierophant_staff/proc/teleport_mob(turf/source, mob/M, turf/target, mob/user) +/obj/item/hierophant_club/proc/teleport_mob(turf/source, mob/M, turf/target, mob/user) var/turf/turf_to_teleport_to = get_step(target, get_dir(source, M)) //get position relative to caster - if(!turf_to_teleport_to || is_blocked_turf(turf_to_teleport_to)) + if(!turf_to_teleport_to || is_blocked_turf(turf_to_teleport_to, TRUE)) return animate(M, alpha = 0, time = 2, easing = EASE_OUT) //fade out sleep(1) @@ -152,19 +233,19 @@ if(user != M) add_attack_logs(user, M, "Teleported from ([source.x],[source.y],[source.z])") -/obj/item/hierophant_staff/proc/cardinal_blasts(turf/T, mob/living/user) //fire cardinal cross blasts with a delay +/obj/item/hierophant_club/proc/cardinal_blasts(turf/T, mob/living/user) //fire cardinal cross blasts with a delay if(!T) return new /obj/effect/temp_visual/hierophant/telegraph/cardinal(T, user) - playsound(T,'sound/magic/blink.ogg', 200, 1) - //playsound(T,'sound/effects/bin_close.ogg', 200, 1) + playsound(T,'sound/effects/bin_close.ogg', 200, TRUE) sleep(2) - new /obj/effect/temp_visual/hierophant/blast(T, user, friendly_fire_check) + var/obj/effect/temp_visual/hierophant/blast/B = new(T, user, friendly_fire_check) + B.damage = HIEROPHANT_CLUB_CARDINAL_DAMAGE + B.monster_damage_boost = FALSE for(var/d in cardinal) - spawn(0) - blast_wall(T, d, user) + INVOKE_ASYNC(src, .proc/blast_wall, T, d, user) -/obj/item/hierophant_staff/proc/blast_wall(turf/T, dir, mob/living/user) //make a wall of blasts blast_range tiles long +/obj/item/hierophant_club/proc/blast_wall(turf/T, dir, mob/living/user) //make a wall of blasts blast_range tiles long if(!T) return var/range = blast_range @@ -173,16 +254,20 @@ for(var/i in 1 to range) if(!J) return - new /obj/effect/temp_visual/hierophant/blast(J, user, friendly_fire_check) + var/obj/effect/temp_visual/hierophant/blast/B = new(J, user, friendly_fire_check) + B.damage = HIEROPHANT_CLUB_CARDINAL_DAMAGE + B.monster_damage_boost = FALSE previousturf = J J = get_step(previousturf, dir) -/obj/item/hierophant_staff/proc/aoe_burst(turf/T, mob/living/user) //make a 3x3 blast around a target +/obj/item/hierophant_club/proc/aoe_burst(turf/T, mob/living/user) //make a 3x3 blast around a target if(!T) return new /obj/effect/temp_visual/hierophant/telegraph(T, user) - playsound(T,'sound/magic/blink.ogg', 200, 1) - //playsound(T,'sound/effects/bin_close.ogg', 200, 1) + playsound(T,'sound/effects/bin_close.ogg', 200, TRUE) sleep(2) for(var/t in RANGE_TURFS(1, T)) - new /obj/effect/temp_visual/hierophant/blast(t, user, friendly_fire_check) + var/obj/effect/temp_visual/hierophant/blast/B = new(t, user, friendly_fire_check) + B.damage = 15 //keeps monster damage boost due to lower damage + +#undef HIEROPHANT_CLUB_CARDINAL_DAMAGE \ No newline at end of file diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 9a7f5c6fe15..ebe71af1f58 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -126,7 +126,6 @@ modkit_design = /datum/design/unique_modkit/bounty /datum/design/unique_modkit - category = list("Mining", "Cyborg Upgrade Modules") //can't be normally obtained build_type = PROTOLATHE | MECHFAB /datum/design/unique_modkit/offensive_turf_aoe @@ -135,6 +134,7 @@ id = "hyperaoemod" materials = list(MAT_METAL = 7000, MAT_GLASS = 3000, MAT_SILVER= 3000, MAT_GOLD = 3000, MAT_DIAMOND = 4000) build_path = /obj/item/borg/upgrade/modkit/aoe/turfs/andmobs + category = list("Mining", "Cyborg Upgrade Modules") /datum/design/unique_modkit/rapid_repeater name = "Kinetic Accelerator Rapid Repeater Mod" @@ -142,6 +142,7 @@ id = "repeatermod" materials = list(MAT_METAL = 5000, MAT_GLASS = 5000, MAT_URANIUM = 8000, MAT_BLUESPACE = 2000) build_path = /obj/item/borg/upgrade/modkit/cooldown/repeater + category = list("Mining", "Cyborg Upgrade Modules") /datum/design/unique_modkit/resonator_blast name = "Kinetic Accelerator Resonator Blast Mod" @@ -149,6 +150,7 @@ id = "resonatormod" materials = list(MAT_METAL = 5000, MAT_GLASS = 5000, MAT_SILVER= 5000, MAT_URANIUM = 5000) build_path = /obj/item/borg/upgrade/modkit/resonator_blasts + category = list("Mining", "Cyborg Upgrade Modules") /datum/design/unique_modkit/bounty name = "Kinetic Accelerator Death Syphon Mod" @@ -156,4 +158,5 @@ id = "bountymod" materials = list(MAT_METAL = 4000, MAT_SILVER = 4000, MAT_GOLD = 4000, MAT_BLUESPACE = 4000) reagents_list = list("blood" = 40) - build_path = /obj/item/borg/upgrade/modkit/bounty \ No newline at end of file + build_path = /obj/item/borg/upgrade/modkit/bounty + category = list("Mining", "Cyborg Upgrade Modules") diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm index 5eb8f5766e4..d7881367d13 100644 --- a/code/modules/mob/living/carbon/alien/alien_defense.dm +++ b/code/modules/mob/living/carbon/alien/alien_defense.dm @@ -59,7 +59,8 @@ In all, this is a lot like the monkey code. /N return 0 /mob/living/carbon/alien/attack_animal(mob/living/simple_animal/M) - if(..()) + . = ..() + if(.) var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) switch(M.melee_damage_type) if(BRUTE) diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 762df8d3b8b..71a3ae3e87c 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -328,7 +328,7 @@ This function restores all organs. return 0 -/mob/living/carbon/human/proc/get_organ(zone) +/mob/living/carbon/human/get_organ(zone) if(!zone) zone = "chest" if(zone in list("eyes", "mouth")) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index af01634de5a..73147afb93a 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -437,16 +437,17 @@ emp_act visible_message("[M] has tried to disarm [src]!") /mob/living/carbon/human/attack_animal(mob/living/simple_animal/M) - if(..()) + . = ..() + if(.) var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) - if(check_shields(M, damage, "the [M.name]", null, MELEE_ATTACK, M.armour_penetration)) - return 0 + if(check_shields(M, damage, "the [M.name]", MELEE_ATTACK, M.armour_penetration)) + return FALSE var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone)) + if(!affecting) + affecting = get_organ("chest") + affecting.add_autopsy_data(M.name, damage) // Add the mob's name to the autopsy data var/armor = run_armor_check(affecting, "melee", armour_penetration = M.armour_penetration) - var/obj/item/organ/external/affected = src.get_organ(dam_zone) - if(affected) - affected.add_autopsy_data(M.name, damage) // Add the mob's name to the autopsy data apply_damage(damage, M.melee_damage_type, affecting, armor) updatehealth("animal attack") diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm index 3c08cfd7bca..b658124bb8f 100644 --- a/code/modules/mob/living/carbon/human/species/_species.dm +++ b/code/modules/mob/living/carbon/human/species/_species.dm @@ -519,7 +519,7 @@ to_chat(M, "You can't use your hand.") return - if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(0, M.name, attack_type = UNARMED_ATTACK)) + if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK)) add_attack_logs(M, H, "Melee attacked with fists (miss/block)") H.visible_message("[M] attempted to touch [H]!") return FALSE diff --git a/code/modules/mob/living/carbon/slime/slime.dm b/code/modules/mob/living/carbon/slime/slime.dm index ddab7eae1ee..4cba9bb5a12 100644 --- a/code/modules/mob/living/carbon/slime/slime.dm +++ b/code/modules/mob/living/carbon/slime/slime.dm @@ -258,7 +258,8 @@ M.updatehealth() /mob/living/carbon/slime/attack_animal(mob/living/simple_animal/M) - if(..()) + . = ..() + if(.) var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) attacked += 10 adjustBruteLoss(damage) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index f0c3b85ece1..f624b7ab33d 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -303,17 +303,17 @@ return /mob/living/attack_animal(mob/living/simple_animal/M) + M.face_atom(src) if((M.a_intent == INTENT_HELP && M.ckey) || M.melee_damage_upper == 0) M.custom_emote(1, "[M.friendly] [src].") - return 0 - else - if(M.attack_sound) - playsound(loc, M.attack_sound, 50, 1, 1) - M.do_attack_animation(src) - visible_message("\The [M] [M.attacktext] [src]!", \ - "\The [M] [M.attacktext] [src]!") - add_attack_logs(M, src, "Animal attacked") - return 1 + return FALSE + if(M.attack_sound) + playsound(loc, M.attack_sound, 50, 1, 1) + M.do_attack_animation(src) + visible_message("\The [M] [M.attacktext] [src]!", \ + "\The [M] [M.attacktext] [src]!") + add_attack_logs(M, src, "Animal attacked") + return TRUE /mob/living/attack_larva(mob/living/carbon/alien/larva/L) switch(L.a_intent) diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 75cca0dfb54..d9f1aac2757 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -227,14 +227,8 @@ // See software.dm for Topic() /mob/living/silicon/pai/attack_animal(mob/living/simple_animal/M) - if((M.a_intent == INTENT_HELP && M.ckey) || M.melee_damage_upper == 0) - M.custom_emote(1, "[M.friendly] [src].") - else - M.do_attack_animation(src) - if(M.attack_sound) - playsound(loc, M.attack_sound, 50, 1, 1) - for(var/mob/O in viewers(src, null)) - O.show_message("[M] [M.attacktext] [src]!", 1) + . = ..() + if(.) var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) add_attack_logs(M, src, "Animal attacked for [damage] damage") adjustBruteLoss(damage) diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index a903b6999dc..d88faff76f6 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -20,7 +20,8 @@ return /mob/living/silicon/attack_animal(mob/living/simple_animal/M) - if(..()) + . = ..() + if(.) var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) switch(M.melee_damage_type) if(BRUTE) diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm index 3fafd7267f2..119eb1b29c0 100644 --- a/code/modules/mob/living/simple_animal/animal_defense.dm +++ b/code/modules/mob/living/simple_animal/animal_defense.dm @@ -53,9 +53,10 @@ L.amount_grown = min(L.amount_grown + damage, L.max_grown) /mob/living/simple_animal/attack_animal(mob/living/simple_animal/M) - if(..()) + . = ..() + if(.) var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) - attack_threshold_check(damage, M.melee_damage_type) + return attack_threshold_check(damage, M.melee_damage_type) /mob/living/simple_animal/attack_slime(mob/living/carbon/slime/M) ..() diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index ea8e80d2e3c..218693b7dc3 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -80,7 +80,7 @@ else to_chat(M, "You cannot repair your own dents, as you have none!") else if(src != M) - ..() + return ..() /mob/living/simple_animal/hostile/construct/narsie_act() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index e7ad2c5fab6..701a75fd3ef 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -30,7 +30,7 @@ Difficulty: Medium icon = 'icons/mob/alienqueen.dmi' light_color = "#E4C7C5" speak_emote = list("roars") - speed = 1 + speed = 3 move_to_delay = 3 projectiletype = /obj/item/projectile/kinetic/miner projectilesound = 'sound/weapons/kenetic_accel.ogg' diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 43274952362..66799ffc6c6 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -1,3 +1,8 @@ +#define BUBBLEGUM_SMASH (health <= maxHealth * 0.5) // angery +#define BUBBLEGUM_CAN_ENRAGE (enrage_till + (enrage_time * 2) <= world.time) +#define BUBBLEGUM_IS_ENRAGED (enrage_till > world.time) + + /* BUBBLEGUM @@ -5,14 +10,16 @@ BUBBLEGUM Bubblegum spawns randomly wherever a lavaland creature is able to spawn. It is the most powerful slaughter demon in existence. Bubblegum's footsteps are heralded by shaking booms, proving its tremendous size. -It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage. +It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power -It tries to strike at its target through any bloodpools under them; if it fails to do that, it will spray blood and then attempt to warp to a bloodpool near the target. -If it fails to warp to a target, it may summon up to 6 slaughterlings from the blood around it. -If it does not summon all 6 slaughterlings, it will instead charge at its target, dealing massive damage to anything it hits and spraying a stream of blood. -At half health, it will either charge three times or warp, then charge, instead of doing a single charge. +It leaves blood trails behind wherever it goes, its clones do as well. +It tries to strike at its target through any bloodpools under them; if it fails to do that. +If it does warp it will enter an enraged state, becoming immune to all projectiles, becoming much faster, and dealing damage and knockback to anything that gets in the cloud around it. +It may summon clones charging from all sides, one of these charges being bubblegum himself. +It can charge at its target, and also heavily damaging anything directly hit in the charge. +If at half health it will start to charge from all sides with clones. -When Bubblegum dies, it leaves behind a chest that can contain three things: +When Bubblegum dies, it leaves behind a H.E.C.K. mining suit as well as a chest that can contain three things: 1. A bottle that, when activated, drives everyone nearby into a frenzy 2. A contract that marks for death the chosen target 3. A spellblade that can slice off limbs at range @@ -37,19 +44,31 @@ Difficulty: Hard armour_penetration = 40 melee_damage_lower = 40 melee_damage_upper = 40 - speed = 1 - move_to_delay = 10 - ranged = 1 + speed = 5 + move_to_delay = 5 + retreat_distance = 5 + minimum_distance = 5 + rapid_melee = 8 // every 1/4 second + melee_queue_distance = 20 // as far as possible really, need this because of blood warp + ranged = TRUE pixel_x = -32 - del_on_death = 1 + del_on_death = TRUE crusher_loot = list(/obj/structure/closet/crate/necropolis/bubblegum/crusher) loot = list(/obj/structure/closet/crate/necropolis/bubblegum) - var/charging = 0 + blood_volume = BLOOD_VOLUME_MAXIMUM //BLEED FOR ME + var/charging = FALSE + var/enrage_till = 0 + var/enrage_time = 70 + var/revving_charge = FALSE internal_type = /obj/item/gps/internal/bubblegum medal_type = BOSS_MEDAL_BUBBLEGUM score_type = BUBBLEGUM_SCORE deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... " death_sound = 'sound/misc/enter_blood.ogg' + attack_action_types = list(/datum/action/innate/megafauna_attack/triple_charge, + /datum/action/innate/megafauna_attack/hallucination_charge, + /datum/action/innate/megafauna_attack/hallucination_surround, + /datum/action/innate/megafauna_attack/blood_warp) /obj/item/gps/internal/bubblegum icon_state = null @@ -57,54 +76,6 @@ Difficulty: Hard desc = "You're not quite sure how a signal can be bloody." invisibility = 100 -/mob/living/simple_animal/hostile/megafauna/bubblegum/adjustBruteLoss(amount) - if(amount > 0 && prob(25)) - var/obj/effect/decal/cleanable/blood/gibs/bubblegum/B = new /obj/effect/decal/cleanable/blood/gibs/bubblegum(loc) - if(prob(40)) - step(B, pick(cardinal)) - else - B.dir = pick(cardinal) - . = ..() - -/obj/effect/decal/cleanable/blood/gibs/bubblegum - name = "thick blood" - desc = "Thick, splattered blood." - random_icon_states = list("gib3", "gib5", "gib6") - amount = 20 - -/obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in() - return TRUE - -/mob/living/simple_animal/hostile/megafauna/bubblegum/Life(seconds, times_fired) - ..() - move_to_delay = Clamp((health/maxHealth) * 10, 5, 10) - -/mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire() - anger_modifier = Clamp(((maxHealth - health)/60),0,20) - if(charging) - return - ranged_cooldown = world.time + ranged_cooldown_time - - var/warped = FALSE - if(!try_bloodattack()) - spawn(0) - blood_spray() - warped = blood_warp() - if(warped && prob(100 - anger_modifier)) - return - - if(prob(90 - anger_modifier) || slaughterlings()) - if(health > maxHealth * 0.5) - spawn(0) - charge() - else - if(prob(70) || warped) - spawn(0) - triple_charge() - else - spawn(0) - warp_charge() - /mob/living/simple_animal/hostile/megafauna/bubblegum/Initialize(mapload) . = ..() if(true_spawn) @@ -112,88 +83,118 @@ Difficulty: Hard if(B != src) qdel(src) //There can be only one return - var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new - AddSpell(bloodspell) - if(istype(loc, /obj/effect/dummy/slaughter)) - bloodspell.phased = 1 -/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon, used_item, no_effect, end_pixel_y) - if(!charging) - ..() +/datum/action/innate/megafauna_attack/triple_charge + name = "Triple Charge" + icon_icon = 'icons/mob/actions/actions.dmi' + button_icon_state = "sniper_zoom" + chosen_message = "You are now triple charging at the target you click on." + chosen_attack_num = 1 -/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget() - if(!charging) - return ..() +/datum/action/innate/megafauna_attack/hallucination_charge + name = "Hallucination Charge" + icon_icon = 'icons/effects/bubblegum.dmi' + button_icon_state = "smack ya one" + chosen_message = "You are now charging with hallucinations at the target you click on." + chosen_attack_num = 2 -/mob/living/simple_animal/hostile/megafauna/bubblegum/Goto(target, delay, minimum_distance) - if(!charging) - ..() +/datum/action/innate/megafauna_attack/hallucination_surround + name = "Surround Target" + icon_icon = 'icons/turf/walls/wall.dmi' + button_icon_state = "wall" + chosen_message = "You are now surrounding the target you click on with hallucinations." + chosen_attack_num = 3 -/mob/living/simple_animal/hostile/megafauna/bubblegum/MoveToTarget(list/possible_targets) - if(!charging) - ..() +/datum/action/innate/megafauna_attack/blood_warp + name = "Blood Warp" + icon_icon = 'icons/effects/blood.dmi' + button_icon_state = "floor1" + chosen_message = "You are now warping to blood around your clicked position." + chosen_attack_num = 4 -/mob/living/simple_animal/hostile/megafauna/bubblegum/Move() +/mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire() if(charging) - new /obj/effect/temp_visual/decoy/fading(loc, src) - DestroySurroundings() - . = ..() - if(!stat && .) - playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1) - if(charging) - DestroySurroundings() + return -/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/warp_charge() - blood_warp() - charge() + anger_modifier = Clamp(((maxHealth - health)/60),0,20) + enrage_time = initial(enrage_time) * Clamp(anger_modifier / 20, 0.5, 1) + ranged_cooldown = world.time + 50 + + if(client) + switch(chosen_attack) + if(1) + triple_charge() + if(2) + hallucination_charge() + if(3) + surround_with_hallucinations() + if(4) + blood_warp() + return + + if(!try_bloodattack() || prob(25 + anger_modifier)) + blood_warp() + + if(!BUBBLEGUM_SMASH) + triple_charge() + else + if(prob(50 + anger_modifier)) + hallucination_charge() + else + surround_with_hallucinations() /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/triple_charge() - charge() - charge() - charge() + charge(delay = 6) + charge(delay = 4) + charge(delay = 2) + SetRecoveryTime(15) -/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge() - var/turf/T = get_turf(target) - if(!T || T == loc) +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/hallucination_charge() + if(!BUBBLEGUM_SMASH || prob(33)) + hallucination_charge_around(times = 6, delay = 8) + SetRecoveryTime(10) + else + hallucination_charge_around(times = 4, delay = 9) + hallucination_charge_around(times = 4, delay = 8) + hallucination_charge_around(times = 4, delay = 7) + triple_charge() + SetRecoveryTime(20) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/surround_with_hallucinations() + for(var/i = 1 to 5) + INVOKE_ASYNC(src, .proc/hallucination_charge_around, 2, 8, 2, 0, 4) + if(ismob(target)) + charge(delay = 6) + else + SLEEP_CHECK_DEATH(6) + SetRecoveryTime(20) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge(atom/chargeat = target, delay = 3, chargepast = 2) + if(!chargeat) return - new /obj/effect/temp_visual/dragon_swoop(T) - charging = 1 + var/chargeturf = get_turf(chargeat) + if(!chargeturf) + return + var/dir = get_dir(src, chargeturf) + var/turf/T = get_ranged_target_turf(chargeturf, dir, chargepast) + if(!T) + return + new /obj/effect/temp_visual/dragon_swoop/bubblegum(T) + charging = TRUE + revving_charge = TRUE DestroySurroundings() walk(src, 0) - dir = get_dir(src, T) - var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc, src) - animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 5) - sleep(5) - throw_at(T, get_dist(src, T), 1, src, 0) - charging = 0 + setDir(dir) + var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc,src) + animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 3) + SLEEP_CHECK_DEATH(delay) + revving_charge = FALSE + var/movespeed = 0.7 + walk_towards(src, T, movespeed) + SLEEP_CHECK_DEATH(get_dist(src, T) * movespeed) + walk(src, 0) // cancel the movement try_bloodattack() - if(target) - Goto(target, move_to_delay, minimum_distance) - - -/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A) - if(charging) - if(isturf(A) || isobj(A) && A.density) - A.ex_act(2) - DestroySurroundings() - ..() - -/mob/living/simple_animal/hostile/megafauna/bubblegum/throw_impact(atom/A) - if(!charging) - return ..() - - else if(isliving(A)) - var/mob/living/L = A - L.visible_message("[src] slams into [L]!", "[src] slams into you!") - L.apply_damage(40, BRUTE) - playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, 1) - shake_camera(L, 4, 3) - shake_camera(src, 2, 3) - var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src))) - L.throw_at(throwtarget, 3) - - charging = 0 - + charging = FALSE /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_mobs_on_blood() var/list/targets = ListTargets() @@ -206,9 +207,7 @@ Difficulty: Hard /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/try_bloodattack() var/list/targets = get_mobs_on_blood() if(targets.len) - spawn(0) - bloodattack(targets, prob(50)) - + INVOKE_ASYNC(src, .proc/bloodattack, targets, prob(50)) return TRUE return FALSE @@ -249,14 +248,14 @@ Difficulty: Hard new /obj/effect/temp_visual/bubblegum_hands/rightsmack(T) else new /obj/effect/temp_visual/bubblegum_hands/leftsmack(T) - sleep(2.5) + SLEEP_CHECK_DEATH(4) for(var/mob/living/L in T) if(!faction_check_mob(L)) to_chat(L, "[src] rends you!") - playsound(T, attack_sound, 100, 1, -1) - var/limb_to_hit = pick("head", "chest", "r_arm", "l_arm", "r_leg", "l_leg") - L.apply_damage(25, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration)) - sleep(3) + playsound(T, attack_sound, 100, TRUE, -1) + var/limb_to_hit = L.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)) + L.apply_damage(10, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration)) + SLEEP_CHECK_DEATH(3) /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodgrab(turf/T, handedness) if(handedness) @@ -265,18 +264,221 @@ Difficulty: Hard else new /obj/effect/temp_visual/bubblegum_hands/leftpaw(T) new /obj/effect/temp_visual/bubblegum_hands/leftthumb(T) - sleep(6) + SLEEP_CHECK_DEATH(6) for(var/mob/living/L in T) if(!faction_check_mob(L)) - to_chat(L, "[src] drags you through the blood!") - playsound(T, 'sound/misc/enter_blood.ogg', 100, 1, -1) - var/turf/targetturf = get_step(src, dir) - L.forceMove(targetturf) - playsound(targetturf, 'sound/misc/exit_blood.ogg', 100, 1, -1) if(L.stat != CONSCIOUS) - spawn(2) - devour(L) - sleep(1) + to_chat(L, "[src] drags you through the blood!") + playsound(T, 'sound/misc/enter_blood.ogg', 100, TRUE, -1) + var/turf/targetturf = get_step(src, dir) + L.forceMove(targetturf) + playsound(targetturf, 'sound/misc/exit_blood.ogg', 100, TRUE, -1) + addtimer(CALLBACK(src, .proc/devour, L), 2) + SLEEP_CHECK_DEATH(1) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_warp() + if(Adjacent(target)) + return FALSE + var/list/can_jaunt = get_pools(get_turf(src), 1) + if(!can_jaunt.len) + return FALSE + + var/list/pools = get_pools(get_turf(target), 5) + var/list/pools_to_remove = get_pools(get_turf(target), 4) + pools -= pools_to_remove + if(!pools.len) + return FALSE + + var/obj/effect/temp_visual/decoy/DA = new /obj/effect/temp_visual/decoy(loc,src) + DA.color = "#FF0000" + var/oldtransform = DA.transform + DA.transform = matrix()*2 + animate(DA, alpha = 255, color = initial(DA.color), transform = oldtransform, time = 3) + SLEEP_CHECK_DEATH(3) + qdel(DA) + + var/obj/effect/decal/cleanable/blood/found_bloodpool + pools = get_pools(get_turf(target), 5) + pools_to_remove = get_pools(get_turf(target), 4) + pools -= pools_to_remove + if(pools.len) + shuffle_inplace(pools) + found_bloodpool = pick(pools) + if(found_bloodpool) + visible_message("[src] sinks into the blood...") + playsound(get_turf(src), 'sound/misc/enter_blood.ogg', 100, TRUE, -1) + forceMove(get_turf(found_bloodpool)) + playsound(get_turf(src), 'sound/misc/exit_blood.ogg', 100, TRUE, -1) + visible_message("And springs back out!") + blood_enrage() + return TRUE + return FALSE + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/be_aggressive() + if(BUBBLEGUM_IS_ENRAGED) + return TRUE + if(isliving(target)) + var/mob/living/livingtarget = target + return (livingtarget.stat != CONSCIOUS || livingtarget.lying) + return FALSE + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_retreat_distance() + return (be_aggressive() ? null : initial(retreat_distance)) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_minimum_distance() + return (be_aggressive() ? 1 : initial(minimum_distance)) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/update_approach() + retreat_distance = get_retreat_distance() + minimum_distance = get_minimum_distance() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage() + if(!BUBBLEGUM_CAN_ENRAGE) + return FALSE + enrage_till = world.time + enrage_time + update_approach() + change_move_delay(3.75) + var/newcolor = rgb(149, 10, 10) + add_atom_colour(newcolor, TEMPORARY_COLOUR_PRIORITY) + var/datum/callback/cb = CALLBACK(src, .proc/blood_enrage_end) + addtimer(cb, enrage_time) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage_end(newcolor = rgb(149, 10, 10)) + update_approach() + change_move_delay() + remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, newcolor) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/change_move_delay(newmove = initial(move_to_delay)) + move_to_delay = newmove + speed = move_to_delay + handle_automated_action() // need to recheck movement otherwise move_to_delay won't update until the next checking aka will be wrong speed for a bit + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_pools(turf/T, range) + . = list() + for(var/obj/effect/decal/cleanable/nearby in view(T, range)) + if(nearby.can_bloodcrawl_in()) + . += nearby + +/obj/effect/decal/cleanable/blood/bubblegum + bloodiness = 0 + +/obj/effect/decal/cleanable/blood/bubblegum/can_bloodcrawl_in() + return TRUE + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/hallucination_charge_around(times = 4, delay = 6, chargepast = 0, useoriginal = 1, radius) + var/startingangle = rand(1, 360) + if(!target) + return + var/turf/chargeat = get_turf(target) + var/srcplaced = FALSE + if(!radius) + radius = times + for(var/i = 1 to times) + var/ang = (startingangle + 360/times * i) + if(!chargeat) + return + var/turf/place = locate(chargeat.x + cos(ang) * radius, chargeat.y + sin(ang) * radius, chargeat.z) + if(!place) + continue + if(!nest || nest && nest.parent && get_dist(nest.parent, place) <= nest_range) + if(!srcplaced && useoriginal) + forceMove(place) + srcplaced = TRUE + continue + var/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/B = new /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination(loc) + B.forceMove(place) + INVOKE_ASYNC(B, .proc/charge, chargeat, delay, chargepast) + if(useoriginal) + charge(chargeat, delay, chargepast) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/adjustBruteLoss(amount, updating_health = TRUE) + . = ..() + if(. > 0 && prob(25)) + var/obj/effect/decal/cleanable/blood/gibs/bubblegum/B = new /obj/effect/decal/cleanable/blood/gibs/bubblegum(loc) + if(prob(40)) + step(B, pick(cardinal)) + else + B.setDir(pick(cardinal)) + +/obj/effect/decal/cleanable/blood/gibs/bubblegum + name = "thick blood" + desc = "Thick, splattered blood." + random_icon_states = list("gib3", "gib5", "gib6") + bloodiness = 20 + +/obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in() + return TRUE + +/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon) + if(!charging) + ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget() + if(!charging) + . = ..() + if(.) + recovery_time = world.time + 20 // can only attack melee once every 2 seconds but rapid_melee gives higher priority + +/mob/living/simple_animal/hostile/megafauna/bubblegum/bullet_act(obj/item/projectile/P) + if(BUBBLEGUM_IS_ENRAGED) + visible_message("[src] deflects the projectile; [p_they()] can't be hit with ranged weapons while enraged!", "You deflect the projectile!") + playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 300, TRUE) + return + ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/ex_act(severity, target) + if(severity >= EXPLODE_LIGHT) + return + severity = EXPLODE_LIGHT // puny mortals + return ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination)) + return TRUE + return ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/Goto(target, delay, minimum_distance) + if(!charging) + ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/MoveToTarget(list/possible_targets) + if(!charging) + ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/Move() + update_approach() + if(revving_charge) + return FALSE + if(charging) + new /obj/effect/temp_visual/decoy/fading(loc,src) + DestroySurroundings() + ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/Moved(atom/OldLoc, Dir, Forced = FALSE) + if(Dir) + new /obj/effect/decal/cleanable/blood/bubblegum(loc) + if(charging) + DestroySurroundings() + playsound(src, 'sound/effects/meteorimpact.ogg', 200, TRUE, 2, TRUE) + return ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A) + if(charging) + if(isturf(A) || isobj(A) && A.density) + A.ex_act(EXPLODE_HEAVY) + DestroySurroundings() + if(isliving(A)) + var/mob/living/L = A + L.visible_message("[src] slams into [L]!", "[src] tramples you into the ground!") + forceMove(get_turf(L)) + L.apply_damage(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination) ? 15 : 30, BRUTE) + playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, TRUE) + shake_camera(L, 4, 3) + shake_camera(src, 2, 3) + ..() + +/obj/effect/temp_visual/dragon_swoop/bubblegum + duration = 10 /obj/effect/temp_visual/bubblegum_hands icon = 'icons/effects/bubblegum.dmi' @@ -290,11 +492,11 @@ Difficulty: Hard /obj/effect/temp_visual/bubblegum_hands/rightpaw icon_state = "rightpawgrab" - layer = MOB_LAYER - 0.1 + layer = BELOW_MOB_LAYER /obj/effect/temp_visual/bubblegum_hands/leftpaw icon_state = "leftpawgrab" - layer = MOB_LAYER - 0.1 + layer = BELOW_MOB_LAYER /obj/effect/temp_visual/bubblegum_hands/rightsmack icon_state = "rightsmack" @@ -302,94 +504,48 @@ Difficulty: Hard /obj/effect/temp_visual/bubblegum_hands/leftsmack icon_state = "leftsmack" -/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_warp() - if(Adjacent(target)) - return FALSE - var/list/can_jaunt = get_pools(get_turf(src), 1) - if(!can_jaunt.len) - return FALSE +/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination + name = "bubblegum's hallucination" + desc = "Is that really just a hallucination?" + health = 1 + maxHealth = 1 + alpha = 127.5 + crusher_loot = null + loot = null + medal_type = null + score_type = null + deathmessage = "Explodes into a pool of blood!" + death_sound = 'sound/effects/splat.ogg' + true_spawn = FALSE - var/list/pools = get_pools(get_turf(target), 2) - var/list/pools_to_remove = get_pools(get_turf(target), 1) - pools -= pools_to_remove - if(!pools.len) - return FALSE +/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Initialize(mapload) + . = ..() + toggle_ai(AI_OFF) - var/obj/effect/temp_visual/decoy/DA = new /obj/effect/temp_visual/decoy(loc, src) - DA.color = "#FF0000" - var/oldtransform = DA.transform - DA.transform = matrix()*2 - animate(DA, alpha = 255, color = initial(DA.color), transform = oldtransform, time = 5) - sleep(5) - qdel(DA) +/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/charge(atom/chargeat = target, delay = 3, chargepast = 2) + ..() + qdel(src) - var/obj/effect/decal/cleanable/blood/found_bloodpool - pools = get_pools(get_turf(target), 2) - pools_to_remove = get_pools(get_turf(target), 1) - pools -= pools_to_remove - if(pools.len) - shuffle(pools) - found_bloodpool = pick(pools) - if(found_bloodpool) - visible_message("[src] sinks into the blood...") - playsound(get_turf(src), 'sound/misc/enter_blood.ogg', 100, 1, -1) - forceMove(get_turf(found_bloodpool)) - playsound(get_turf(src), 'sound/misc/exit_blood.ogg', 100, 1, -1) - visible_message("And springs back out!") +/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Destroy() + new /obj/effect/decal/cleanable/blood(get_turf(src)) + . = ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum)) // hallucinations should not be stopping bubblegum or eachother return TRUE - return FALSE + return ..() -/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_pools(turf/T, range) - . = list() - for(var/obj/effect/decal/cleanable/nearby in view(T, range)) - if(nearby.can_bloodcrawl_in()) - . += nearby +/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Life() + return -/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_spray() - visible_message("[src] sprays a stream of gore!") - var/range = 6 + round(anger_modifier * 0.4) - var/turf/previousturf = get_turf(src) - var/turf/J = previousturf - var/targetdir = get_dir(src, target) - if(target.loc == loc) - targetdir = dir - face_atom(target) - new /obj/effect/decal/cleanable/blood/bubblegum(J) - for(var/i in 1 to range) - J = get_step(previousturf, targetdir) - new /obj/effect/temp_visual/dir_setting/bloodsplatter(previousturf, get_dir(previousturf, J)) - playsound(previousturf,'sound/effects/splat.ogg', 100, 1, -1) - if(!J || !previousturf.CanAtmosPass(J)) - break - new /obj/effect/decal/cleanable/blood/bubblegum(J) - previousturf = J - sleep(1) +/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/adjustBruteLoss(amount, updating_health = TRUE) + return -/obj/effect/decal/cleanable/blood/bubblegum - amount = 0 +/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/OpenFire() + return -/obj/effect/decal/cleanable/blood/bubblegum/can_bloodcrawl_in() - return TRUE +/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/AttackingTarget() + return -/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/slaughterlings() - visible_message("[src] summons a shoal of slaughterlings!") - var/max_amount = 6 - for(var/H in get_pools(get_turf(src), 1)) - if(!max_amount) - break - max_amount-- - var/obj/effect/decal/cleanable/blood/B = H - new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter(B.loc) - return max_amount - -/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter - name = "slaughterling" - desc = "Though not yet strong enough to create a true physical form, it's nonetheless determined to murder you." - density = 0 - faction = list("mining", "boss") - weather_immunities = list("lava","ash") - -/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter/CanPass(atom/movable/mover, turf/target, height = 0) - if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum)) - return 1 - return 0 +/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/try_bloodattack() + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index f275f7ce7d0..8eed750804f 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -1,4 +1,3 @@ - /* COLOSSUS @@ -31,35 +30,74 @@ Difficulty: Very Hard attack_sound = 'sound/magic/ratvar_attack.ogg' icon_state = "eva" icon_living = "eva" - icon_dead = "dragon_dead" + icon_dead = "" friendly = "stares down" icon = 'icons/mob/lavaland/96x96megafauna.dmi' speak_emote = list("roars") armour_penetration = 40 melee_damage_lower = 40 melee_damage_upper = 40 - speed = 1 + speed = 10 move_to_delay = 10 - ranged = 1 + ranged = TRUE pixel_x = -32 - del_on_death = 1 + del_on_death = TRUE internal_type = /obj/item/gps/internal/colossus medal_type = BOSS_MEDAL_COLOSSUS score_type = COLOSSUS_SCORE crusher_loot = list(/obj/structure/closet/crate/necropolis/colossus/crusher) loot = list(/obj/structure/closet/crate/necropolis/colossus) - butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30) deathmessage = "disintegrates, leaving a glowing core in its wake." death_sound = 'sound/misc/demon_dies.ogg' + attack_action_types = list(/datum/action/innate/megafauna_attack/spiral_attack, + /datum/action/innate/megafauna_attack/aoe_attack, + /datum/action/innate/megafauna_attack/shotgun, + /datum/action/innate/megafauna_attack/alternating_cardinals) -/mob/living/simple_animal/hostile/megafauna/colossus/devour(mob/living/L) - visible_message("[src] disintegrates [L]!") - L.dust() +/datum/action/innate/megafauna_attack/spiral_attack + name = "Spiral Shots" + icon_icon = 'icons/mob/actions/actions.dmi' + button_icon_state = "sniper_zoom" + chosen_message = "You are now firing in a spiral." + chosen_attack_num = 1 + +/datum/action/innate/megafauna_attack/aoe_attack + name = "All Directions" + icon_icon = 'icons/effects/effects.dmi' + button_icon_state = "at_shield2" + chosen_message = "You are now firing in all directions." + chosen_attack_num = 2 + +/datum/action/innate/megafauna_attack/shotgun + name = "Shotgun Fire" + icon_icon = 'icons/obj/guns/projectile.dmi' + button_icon_state = "shotgun" + chosen_message = "You are now firing shotgun shots where you aim." + chosen_attack_num = 3 + +/datum/action/innate/megafauna_attack/alternating_cardinals + name = "Alternating Shots" + icon_icon = 'icons/obj/guns/projectile.dmi' + button_icon_state = "pistol" + chosen_message = "You are now firing in alternating cardinal directions." + chosen_attack_num = 4 /mob/living/simple_animal/hostile/megafauna/colossus/OpenFire() anger_modifier = Clamp(((maxHealth - health)/50),0,20) ranged_cooldown = world.time + 120 + if(client) + switch(chosen_attack) + if(1) + select_spiral_attack() + if(2) + random_shots() + if(3) + blast() + if(4) + alternating_dir_shots() + return + if(enrage(target)) if(move_to_delay == initial(move_to_delay)) visible_message("\"You can't dodge.\"") @@ -72,26 +110,111 @@ Difficulty: Very Hard move_to_delay = initial(move_to_delay) if(prob(20+anger_modifier)) //Major attack - telegraph() - - if(health < maxHealth/3) - double_spiral() - else - visible_message("\"Judgement.\"") - spawn(0) - spiral_shoot(rand(0, 1)) - + select_spiral_attack() else if(prob(20)) - ranged_cooldown = world.time + 30 random_shots() else if(prob(70)) - ranged_cooldown = world.time + 20 blast() else - ranged_cooldown = world.time + 40 - spawn(0) - alternating_dir_shots() + alternating_dir_shots() + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/L) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.martial_art && prob(H.martial_art.deflection_chance)) + . = TRUE + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots() + ranged_cooldown = world.time + 40 + dir_shots(diagonals) + SLEEP_CHECK_DEATH(10) + dir_shots(cardinal) + SLEEP_CHECK_DEATH(10) + dir_shots(diagonals) + SLEEP_CHECK_DEATH(10) + dir_shots(cardinal) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/select_spiral_attack() + telegraph() + if(health < maxHealth/3) + return double_spiral() + visible_message("\"Judgement.\"") + return spiral_shoot() + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/double_spiral() + visible_message("\"Die.\"") + + SLEEP_CHECK_DEATH(10) + INVOKE_ASYNC(src, .proc/spiral_shoot, FALSE) + INVOKE_ASYNC(src, .proc/spiral_shoot, TRUE) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/spiral_shoot(negative = pick(TRUE, FALSE), counter_start = 8) + var/turf/start_turf = get_step(src, pick(alldirs)) + var/counter = counter_start + for(var/i in 1 to 80) + if(negative) + counter-- + else + counter++ + if(counter > 16) + counter = 1 + if(counter < 1) + counter = 16 + shoot_projectile(start_turf, counter * 22.5) + playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE) + SLEEP_CHECK_DEATH(1) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/shoot_projectile(turf/marker, set_angle) + if(!isnum(set_angle) && (!marker || marker == loc)) + return + var/turf/startloc = get_turf(src) + var/obj/item/projectile/P = new /obj/item/projectile/colossus(startloc) + P.preparePixelProjectile(marker, marker, startloc) + P.firer = src + if(target) + P.original = target + P.fire(set_angle) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/random_shots() + ranged_cooldown = world.time + 30 + var/turf/U = get_turf(src) + playsound(U, 'sound/magic/clockwork/invoke_general.ogg', 300, TRUE, 5) + for(var/T in RANGE_TURFS(12, U) - U) + if(prob(5)) + shoot_projectile(T) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/blast(set_angle) + ranged_cooldown = world.time + 20 + var/turf/target_turf = get_turf(target) + playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 2) + newtonian_move(get_dir(target_turf, src)) + var/angle_to_target = Get_Angle(src, target_turf) + if(isnum(set_angle)) + angle_to_target = set_angle + var/static/list/colossus_shotgun_shot_angles = list(12.5, 7.5, 2.5, -2.5, -7.5, -12.5) + for(var/i in colossus_shotgun_shot_angles) + shoot_projectile(target_turf, angle_to_target + i) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/dir_shots(list/dirs) + if(!islist(dirs)) + dirs = alldirs.Copy() + playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 2) + for(var/d in dirs) + var/turf/E = get_step(src, d) + shoot_projectile(E) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/telegraph() + for(var/mob/M in range(10,src)) + if(M.client) + flash_color(M.client, "#C80000", 1) + shake_camera(M, 4, 3) + playsound(src, 'sound/magic/narsie_attack.ogg', 200, TRUE) + + +/mob/living/simple_animal/hostile/megafauna/colossus/devour(mob/living/L) + visible_message("[src] disintegrates [L]!") + L.dust() /obj/effect/temp_visual/at_shield name = "anti-toolbox field" @@ -99,15 +222,14 @@ Difficulty: Very Hard icon = 'icons/effects/effects.dmi' icon_state = "at_shield2" layer = FLY_LAYER - luminosity = 2 + light_range = 2 duration = 8 var/target -/obj/effect/temp_visual/at_shield/New(new_loc, new_target) - ..() +/obj/effect/temp_visual/at_shield/Initialize(mapload, new_target) + . = ..() target = new_target - spawn(0) - orbit(target, 0, FALSE, 0, 0, FALSE, TRUE) + INVOKE_ASYNC(src, /atom/movable/proc/orbit, target, 0, FALSE, 0, 0, FALSE, TRUE) /mob/living/simple_animal/hostile/megafauna/colossus/bullet_act(obj/item/projectile/P) if(!stat) @@ -117,126 +239,7 @@ Difficulty: Very Hard var/random_y = rand(0, 72) AT.pixel_y += random_y - ..() - -/mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/L) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.martial_art && prob(H.martial_art.deflection_chance)) - . = TRUE - -/mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots() - dir_shots(diagonals) - sleep(10) - dir_shots(cardinal) - sleep(10) - dir_shots(diagonals) - sleep(10) - dir_shots(cardinal) - -/mob/living/simple_animal/hostile/megafauna/colossus/proc/double_spiral() - visible_message("\"Die.\"") - - sleep(10) - spawn(0) - spiral_shoot() - spawn(0) - spiral_shoot(1) - -/mob/living/simple_animal/hostile/megafauna/colossus/proc/spiral_shoot(negative = 0, counter_start = 1) - var/counter = counter_start - var/turf/marker - for(var/i in 1 to 80) - switch(counter) - if(1) - marker = locate(x, y - 2, z) - if(2) - marker = locate(x - 1, y - 2, z) - if(3) - marker = locate(x - 2, y - 2, z) - if(4) - marker = locate(x - 2, y - 1, z) - if(5) - marker = locate(x - 2, y, z) - if(6) - marker = locate(x - 2, y + 1, z) - if(7) - marker = locate(x - 2, y + 2, z) - if(8) - marker = locate(x - 1, y + 2, z) - if(9) - marker = locate(x, y + 2, z) - if(10) - marker = locate(x + 1, y + 2, z) - if(11) - marker = locate(x + 2, y + 2, z) - if(12) - marker = locate(x + 2, y + 1, z) - if(13) - marker = locate(x + 2, y, z) - if(14) - marker = locate(x + 2, y - 1, z) - if(15) - marker = locate(x + 2, y - 2, z) - if(16) - marker = locate(x + 1, y - 2, z) - - if(negative) - counter-- - else - counter++ - if(counter > 16) - counter = 1 - if(counter < 1) - counter = 16 - shoot_projectile(marker) - playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 20, 1) - sleep(1) - -/mob/living/simple_animal/hostile/megafauna/colossus/proc/shoot_projectile(turf/marker) - if(!marker || marker == loc) - return - var/turf/startloc = get_turf(src) - var/obj/item/projectile/P = new /obj/item/projectile/colossus(startloc) - P.current = startloc - P.starting = startloc - P.firer = src - P.yo = marker.y - startloc.y - P.xo = marker.x - startloc.x - if(target) - P.original = target - else - P.original = marker - P.fire() - -/mob/living/simple_animal/hostile/megafauna/colossus/proc/random_shots() - var/turf/U = get_turf(src) - playsound(U, 'sound/magic/invoke_general.ogg', 300, 1, 5) - for(var/T in RANGE_TURFS(12, U) - U) - if(prob(5)) - shoot_projectile(T) - -/mob/living/simple_animal/hostile/megafauna/colossus/proc/blast() - playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 200, 1, 2) - var/turf/T = get_turf(target) - newtonian_move(get_dir(T, targets_from)) - for(var/turf/turf in range(1, T)) - shoot_projectile(turf) - -/mob/living/simple_animal/hostile/megafauna/colossus/proc/dir_shots(list/dirs) - if(!islist(dirs)) - dirs = alldirs.Copy() - playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 200, 1, 2) - for(var/d in dirs) - var/turf/E = get_step(src, d) - shoot_projectile(E) - -/mob/living/simple_animal/hostile/megafauna/colossus/proc/telegraph() - for(var/mob/M in range(10,src)) - if(M.client) - flash_color(M.client, rgb(200, 0, 0), 1) - shake_camera(M, 4, 3) - playsound(get_turf(src),'sound/magic/narsie_attack.ogg', 200, 1) + return ..() /obj/item/projectile/colossus name ="death bolt" diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 4ac58ecf0b5..8b6d6fed947 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -1,3 +1,9 @@ +#define DRAKE_SWOOP_HEIGHT 270 //how high up drakes go, in pixels +#define DRAKE_SWOOP_DIRECTION_CHANGE_RANGE 5 //the range our x has to be within to not change the direction we slam from + +#define SWOOP_DAMAGEABLE 1 +#define SWOOP_INVULNERABLE 2 + /* ASH DRAKE @@ -6,10 +12,11 @@ Ash drakes spawn randomly wherever a lavaland creature is able to spawn. They ar It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage. -Whenever possible, the drake will breathe fire in the four cardinal directions, igniting and heavily damaging anything caught in the blast. -It also often causes fire to rain from the sky - many nearby turfs will flash red as a fireball crashes into them, dealing damage to anything on the turfs. -The drake also utilizes its wings to fly into the sky and crash down onto a specified point. Anything on this point takes tremendous damage. +Whenever possible, the drake will breathe fire directly at it's target, igniting and heavily damaging anything caught in the blast. +It also often causes lava to pool from the ground around you - many nearby turfs will temporarily turn into lava, dealing damage to anything on the turfs. +The drake also utilizes its wings to fly into the sky, flying after its target and attempting to slam down on them. Anything near when it slams down takes huge damage. - Sometimes it will chain these swooping attacks over and over, making swiftness a necessity. + - Sometimes, it will encase its target in an arena of lava When an ash drake dies, it leaves behind a chest that can contain four things: 1. A spectral blade that allows its wielder to call ghosts to it, enhancing its power @@ -39,31 +46,358 @@ Difficulty: Medium armour_penetration = 40 melee_damage_lower = 40 melee_damage_upper = 40 - speed = 1 - move_to_delay = 10 - ranged = 1 + speed = 5 + move_to_delay = 5 + ranged = TRUE pixel_x = -16 crusher_loot = list(/obj/structure/closet/crate/necropolis/dragon/crusher) loot = list(/obj/structure/closet/crate/necropolis/dragon) butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30) - var/swooping = 0 - var/swoop_cooldown = 0 + var/swooping = NONE + var/player_cooldown = 0 internal_type = /obj/item/gps/internal/dragon medal_type = BOSS_MEDAL_DRAKE score_type = DRAKE_SCORE deathmessage = "collapses into a pile of bones, its flesh sloughing away." death_sound = 'sound/misc/demon_dies.ogg' + attack_action_types = list(/datum/action/innate/megafauna_attack/fire_cone, + /datum/action/innate/megafauna_attack/fire_cone_meteors, + /datum/action/innate/megafauna_attack/mass_fire, + /datum/action/innate/megafauna_attack/lava_swoop) + +/datum/action/innate/megafauna_attack/fire_cone + name = "Fire Cone" + icon_icon = 'icons/obj/wizard.dmi' + button_icon_state = "fireball" + chosen_message = "You are now shooting fire at your target." + chosen_attack_num = 1 + +/datum/action/innate/megafauna_attack/fire_cone_meteors + name = "Fire Cone With Meteors" + icon_icon = 'icons/mob/actions/actions.dmi' + button_icon_state = "sniper_zoom" + chosen_message = "You are now shooting fire at your target and raining fire around you." + chosen_attack_num = 2 + +/datum/action/innate/megafauna_attack/mass_fire + name = "Mass Fire Attack" + icon_icon = 'icons/effects/fire.dmi' + button_icon_state = "1" + chosen_message = "You are now shooting mass fire at your target." + chosen_attack_num = 3 + +/datum/action/innate/megafauna_attack/lava_swoop + name = "Lava Swoop" + icon_icon = 'icons/effects/effects.dmi' + button_icon_state = "lavastaff_warn" + chosen_message = "You are now swooping and raining lava at your target." + chosen_attack_num = 4 + +/obj/item/gps/internal/dragon + icon_state = null + gpstag = "Fiery Signal" + desc = "Here there be dragons." + invisibility = 100 + +/mob/living/simple_animal/hostile/megafauna/dragon/OpenFire() + if(swooping) + return + + anger_modifier = Clamp(((maxHealth - health)/50),0,20) + ranged_cooldown = world.time + ranged_cooldown_time + + if(client) + switch(chosen_attack) + if(1) + fire_cone(meteors = FALSE) + if(2) + fire_cone() + if(3) + mass_fire() + if(4) + lava_swoop() + return + + if(prob(15 + anger_modifier)) + lava_swoop() + + else if(prob(10+anger_modifier)) + shoot_fire_attack() + else + fire_cone() + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/shoot_fire_attack() + if(health < maxHealth*0.5) + mass_fire() + else + fire_cone() + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_rain() + if(!target) + return + target.visible_message("Fire rains from the sky!") + for(var/turf/turf in range(9,get_turf(target))) + if(prob(11)) + new /obj/effect/temp_visual/target(turf) + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_pools(var/amount, var/delay = 0.8) + if(!target) + return + target.visible_message("Lava starts to pool up around you!") + while(amount > 0) + if(QDELETED(target)) + break + var/turf/T = pick(RANGE_TURFS(1, target)) + new /obj/effect/temp_visual/lava_warning(T, 60) // longer reset time for the lava + amount-- + SLEEP_CHECK_DEATH(delay) + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_swoop(var/amount = 30) + if(health < maxHealth * 0.5) + return swoop_attack(lava_arena = TRUE, swoop_cooldown = 60) + INVOKE_ASYNC(src, .proc/lava_pools, amount) + swoop_attack(FALSE, target, 1000) // longer cooldown until it gets reset below + SLEEP_CHECK_DEATH(0) + fire_cone() + if(health < maxHealth*0.5) + SLEEP_CHECK_DEATH(10) + fire_cone() + SLEEP_CHECK_DEATH(10) + fire_cone() + SetRecoveryTime(40) + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/mass_fire(var/spiral_count = 12, var/range = 15, var/times = 3) + SLEEP_CHECK_DEATH(0) + for(var/i = 1 to times) + SetRecoveryTime(50) + playsound(get_turf(src),'sound/magic/fireball.ogg', 200, TRUE) + var/increment = 360 / spiral_count + for(var/j = 1 to spiral_count) + var/list/turfs = line_target(j * increment + i * increment / 2, range, src) + INVOKE_ASYNC(src, .proc/fire_line, turfs) + SLEEP_CHECK_DEATH(25) + SetRecoveryTime(30) + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_arena() + if(!target) + return + target.visible_message("[src] encases you in an arena of fire!") + var/amount = 3 + var/turf/center = get_turf(target) + var/list/walled = RANGE_TURFS(3, center) - RANGE_TURFS(2, center) + var/list/drakewalls = list() + for(var/turf/T in walled) + drakewalls += new /obj/effect/temp_visual/drakewall(T) // no people with lava immunity can just run away from the attack for free + var/list/indestructible_turfs = list() + for(var/turf/T in RANGE_TURFS(2, center)) + if(istype(T, /turf/simulated/floor/indestructible)) + continue + if(!istype(T, /turf/simulated/wall/indestructible)) + T.ChangeTurf(/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface) + else + indestructible_turfs += T + SLEEP_CHECK_DEATH(10) // give them a bit of time to realize what attack is actually happening + + var/list/turfs = RANGE_TURFS(2, center) + while(amount > 0) + var/list/empty = indestructible_turfs.Copy() // can't place safe turfs on turfs that weren't changed to be open + var/any_attack = 0 + for(var/turf/T in turfs) + for(var/mob/living/L in T.contents) + if(L.client) + empty += pick(((RANGE_TURFS(2, L) - RANGE_TURFS(1, L)) & turfs) - empty) // picks a turf within 2 of the creature not outside or in the shield + any_attack = 1 + for(var/obj/mecha/M in T.contents) + empty += pick(((RANGE_TURFS(2, M) - RANGE_TURFS(1, M)) & turfs) - empty) + any_attack = 1 + if(!any_attack) + for(var/obj/effect/temp_visual/drakewall/D in drakewalls) + qdel(D) + return 0 // nothing to attack in the arena time for enraged attack if we still have a target + for(var/turf/T in turfs) + if(!(T in empty)) + new /obj/effect/temp_visual/lava_warning(T) + else if(!istype(T, /turf/simulated/wall/indestructible)) + new /obj/effect/temp_visual/lava_safe(T) + amount-- + SLEEP_CHECK_DEATH(24) + return 1 // attack finished completely + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/arena_escape_enrage() // you ran somehow / teleported away from my arena attack now i'm mad fucker + SLEEP_CHECK_DEATH(0) + SetRecoveryTime(80) + visible_message("[src] starts to glow vibrantly as its wounds close up!") + adjustBruteLoss(-250) // yeah you're gonna pay for that, don't run nerd + add_atom_colour(rgb(255, 255, 0), TEMPORARY_COLOUR_PRIORITY) + move_to_delay = move_to_delay / 2 + light_range = 10 + SLEEP_CHECK_DEATH(10) // run. + mass_fire(20, 15, 3) + move_to_delay = initial(move_to_delay) + remove_atom_colour(TEMPORARY_COLOUR_PRIORITY) + light_range = initial(light_range) + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_cone(var/atom/at = target, var/meteors = TRUE) + playsound(get_turf(src),'sound/magic/fireball.ogg', 200, TRUE) + SLEEP_CHECK_DEATH(0) + if(prob(50) && meteors) + INVOKE_ASYNC(src, .proc/fire_rain) + var/range = 15 + var/list/turfs = list() + turfs = line_target(-40, range, at) + INVOKE_ASYNC(src, .proc/fire_line, turfs) + turfs = line_target(0, range, at) + INVOKE_ASYNC(src, .proc/fire_line, turfs) + turfs = line_target(40, range, at) + INVOKE_ASYNC(src, .proc/fire_line, turfs) + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/line_target(var/offset, var/range, var/atom/at = target) + if(!at) + return + var/angle = Atan2(at.x - src.x, at.y - src.y) + offset + var/turf/T = get_turf(src) + for(var/i in 1 to range) + var/turf/check = locate(src.x + cos(angle) * i, src.y + sin(angle) * i, src.z) + if(!check) + break + T = check + return (getline(src, T) - get_turf(src)) + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_line(var/list/turfs) + SLEEP_CHECK_DEATH(0) + dragon_fire_line(src, turfs) + +//fire line keeps going even if dragon is deleted +/proc/dragon_fire_line(var/source, var/list/turfs) + var/list/hit_list = list() + for(var/turf/T in turfs) + if(T.density) + break + new /obj/effect/hotspot(T) + T.hotspot_expose(700,50,1) + for(var/mob/living/L in T.contents) + if(L in hit_list || L == source) + continue + hit_list += L + L.adjustFireLoss(20) + to_chat(L, "You're hit by [source]'s fire breath!") + + // deals damage to mechs + for(var/obj/mecha/M in T.contents) + if(M in hit_list) + continue + hit_list += M + M.take_damage(45, BRUTE, "melee", 1) + sleep(1.5) + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/swoop_attack(lava_arena = FALSE, atom/movable/manual_target, var/swoop_cooldown = 30) + if(stat || swooping) + return + if(manual_target) + target = manual_target + if(!target) + return + stop_automated_movement = TRUE + swooping |= SWOOP_DAMAGEABLE + density = FALSE + icon_state = "shadow" + visible_message("[src] swoops up high!") + + var/negative + var/initial_x = x + if(target.x < initial_x) //if the target's x is lower than ours, swoop to the left + negative = TRUE + else if(target.x > initial_x) + negative = FALSE + else if(target.x == initial_x) //if their x is the same, pick a direction + negative = prob(50) + var/obj/effect/temp_visual/dragon_flight/F = new /obj/effect/temp_visual/dragon_flight(loc, negative) + + negative = !negative //invert it for the swoop down later + + var/oldtransform = transform + alpha = 255 + animate(src, alpha = 204, transform = matrix()*0.9, time = 3, easing = BOUNCE_EASING) + for(var/i in 1 to 3) + sleep(1) + if(QDELETED(src) || stat == DEAD) //we got hit and died, rip us + qdel(F) + if(stat == DEAD) + swooping &= ~SWOOP_DAMAGEABLE + animate(src, alpha = 255, transform = oldtransform, time = 0, flags = ANIMATION_END_NOW) //reset immediately + return + animate(src, alpha = 100, transform = matrix()*0.7, time = 7) + swooping |= SWOOP_INVULNERABLE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + SLEEP_CHECK_DEATH(7) + + while(target && loc != get_turf(target)) + forceMove(get_step(src, get_dir(src, target))) + SLEEP_CHECK_DEATH(0.5) + + // Ash drake flies onto its target and rains fire down upon them + var/descentTime = 10 + var/lava_success = TRUE + if(lava_arena) + lava_success = lava_arena() + + + //ensure swoop direction continuity. + if(negative) + if(IsInRange(x, initial_x + 1, initial_x + DRAKE_SWOOP_DIRECTION_CHANGE_RANGE)) + negative = FALSE + else + if(IsInRange(x, initial_x - DRAKE_SWOOP_DIRECTION_CHANGE_RANGE, initial_x - 1)) + negative = TRUE + new /obj/effect/temp_visual/dragon_flight/end(loc, negative) + new /obj/effect/temp_visual/dragon_swoop(loc) + animate(src, alpha = 255, transform = oldtransform, descentTime) + SLEEP_CHECK_DEATH(descentTime) + swooping &= ~SWOOP_INVULNERABLE + mouse_opacity = initial(mouse_opacity) + icon_state = "dragon" + playsound(loc, 'sound/effects/meteorimpact.ogg', 200, TRUE) + for(var/mob/living/L in orange(1, src)) + if(L.stat) + visible_message("[src] slams down on [L], crushing [L.p_them()]!") + L.gib() + else + L.adjustBruteLoss(75) + if(L && !QDELETED(L)) // Some mobs are deleted on death + var/throw_dir = get_dir(src, L) + if(L.loc == loc) + throw_dir = pick(alldirs) + var/throwtarget = get_edge_target_turf(src, throw_dir) + L.throw_at(throwtarget, 3) + visible_message("[L] is thrown clear of [src]!") + for(var/obj/mecha/M in orange(1, src)) + M.take_damage(75, BRUTE, "melee", 1) + + for(var/mob/M in range(7, src)) + shake_camera(M, 15, 1) + + density = TRUE + SLEEP_CHECK_DEATH(1) + swooping &= ~SWOOP_DAMAGEABLE + SetRecoveryTime(swoop_cooldown) + if(!lava_success) + arena_escape_enrage() /mob/living/simple_animal/hostile/megafauna/dragon/ex_act(severity, target) - if(severity == 3) + if(severity == EXPLODE_LIGHT) return ..() /mob/living/simple_animal/hostile/megafauna/dragon/adjustHealth(amount, updating_health = TRUE) - if(swooping) + if(swooping & SWOOP_INVULNERABLE) return FALSE return ..() +/mob/living/simple_animal/hostile/megafauna/dragon/visible_message() + if(swooping & SWOOP_INVULNERABLE) //to suppress attack messages without overriding every single proc that could send a message saying we got hit + return + return ..() + /mob/living/simple_animal/hostile/megafauna/dragon/AttackingTarget() if(!swooping) return ..() @@ -83,181 +417,161 @@ Difficulty: Medium /mob/living/simple_animal/hostile/megafauna/dragon/Process_Spacemove(movement_dir = 0) return 1 -/obj/effect/temp_visual/fireball - icon = 'icons/obj/wizard.dmi' - icon_state = "fireball" - name = "fireball" - desc = "Get out of the way!" - layer = 6 - randomdir = 0 - duration = 12 - pixel_z = 500 +/obj/effect/temp_visual/lava_warning + icon_state = "lavastaff_warn" + layer = BELOW_MOB_LAYER + light_range = 2 + duration = 13 -/obj/effect/temp_visual/fireball/New(loc) - ..() - animate(src, pixel_z = 0, time = 12) +/obj/effect/temp_visual/lava_warning/ex_act() + return -/obj/effect/temp_visual/target - icon = 'icons/mob/actions/actions.dmi' - icon_state = "sniper_zoom" - layer = MOB_LAYER - 0.1 - luminosity = 2 - duration = 12 +/obj/effect/temp_visual/lava_warning/Initialize(mapload, var/reset_time = 10) + . = ..() + INVOKE_ASYNC(src, .proc/fall, reset_time) + src.alpha = 63.75 + animate(src, alpha = 255, time = duration) + +/obj/effect/temp_visual/lava_warning/proc/fall(var/reset_time) + var/turf/T = get_turf(src) + playsound(T,'sound/magic/fleshtostone.ogg', 80, TRUE) + sleep(duration) + playsound(T,'sound/magic/fireball.ogg', 200, TRUE) + + for(var/mob/living/L in T.contents) + if(istype(L, /mob/living/simple_animal/hostile/megafauna/dragon)) + continue + L.adjustFireLoss(10) + to_chat(L, "You fall directly into the pool of lava!") + + // deals damage to mechs + for(var/obj/mecha/M in T.contents) + M.take_damage(45, BRUTE, "melee", 1) + + // changes turf to lava temporarily + if(!T.density && !istype(T, /turf/simulated/floor/plating/lava)) + var/lava_turf = /turf/simulated/floor/plating/lava/smooth + var/reset_turf = T.type + T.ChangeTurf(lava_turf) + sleep(reset_time) + T.ChangeTurf(reset_turf) + +/obj/effect/temp_visual/drakewall + desc = "An ash drakes true flame." + name = "Fire Barrier" + icon = 'icons/effects/fire.dmi' + icon_state = "1" + anchored = TRUE + opacity = 0 + density = TRUE + duration = 82 + color = COLOR_DARK_ORANGE + +/obj/effect/temp_visual/drakewall/CanAtmosPass() + return !density + +/obj/effect/temp_visual/lava_safe + icon = 'icons/obj/hand_of_god_structures.dmi' + icon_state = "trap-earth" + layer = BELOW_MOB_LAYER + light_range = 2 + duration = 13 /obj/effect/temp_visual/dragon_swoop name = "certain death" desc = "Don't just stand there, move!" icon = 'icons/effects/96x96.dmi' icon_state = "landing" - layer = MOB_LAYER - 0.1 + layer = BELOW_MOB_LAYER pixel_x = -32 pixel_y = -32 color = "#FF0000" duration = 10 +/obj/effect/temp_visual/dragon_flight + icon = 'icons/mob/lavaland/64x64megafauna.dmi' + icon_state = "dragon" + layer = ABOVE_ALL_MOB_LAYER + pixel_x = -16 + duration = 10 + randomdir = FALSE + +/obj/effect/temp_visual/dragon_flight/Initialize(mapload, negative) + . = ..() + INVOKE_ASYNC(src, .proc/flight, negative) + +/obj/effect/temp_visual/dragon_flight/proc/flight(negative) + if(negative) + animate(src, pixel_x = -DRAKE_SWOOP_HEIGHT*0.1, pixel_z = DRAKE_SWOOP_HEIGHT*0.15, time = 3, easing = BOUNCE_EASING) + else + animate(src, pixel_x = DRAKE_SWOOP_HEIGHT*0.1, pixel_z = DRAKE_SWOOP_HEIGHT*0.15, time = 3, easing = BOUNCE_EASING) + sleep(3) + icon_state = "swoop" + if(negative) + animate(src, pixel_x = -DRAKE_SWOOP_HEIGHT, pixel_z = DRAKE_SWOOP_HEIGHT, time = 7) + else + animate(src, pixel_x = DRAKE_SWOOP_HEIGHT, pixel_z = DRAKE_SWOOP_HEIGHT, time = 7) + +/obj/effect/temp_visual/dragon_flight/end + pixel_x = DRAKE_SWOOP_HEIGHT + pixel_z = DRAKE_SWOOP_HEIGHT + duration = 10 + +/obj/effect/temp_visual/dragon_flight/end/flight(negative) + if(negative) + pixel_x = -DRAKE_SWOOP_HEIGHT + animate(src, pixel_x = -16, pixel_z = 0, time = 5) + else + animate(src, pixel_x = -16, pixel_z = 0, time = 5) + +obj/effect/temp_visual/fireball + icon = 'icons/obj/wizard.dmi' + icon_state = "fireball" + name = "fireball" + desc = "Get out of the way!" + layer = FLY_LAYER + randomdir = FALSE + duration = 9 + pixel_z = 270 + +/obj/effect/temp_visual/fireball/Initialize() + . = ..() + animate(src, pixel_z = 0, time = duration) + +/obj/effect/temp_visual/target + icon = 'icons/mob/actions/actions.dmi' + icon_state = "sniper_zoom" + layer = BELOW_MOB_LAYER + light_range = 2 + duration = 9 + /obj/effect/temp_visual/target/ex_act() return -/obj/effect/temp_visual/target/New(loc) - ..() - spawn(0) - fall() +/obj/effect/temp_visual/target/Initialize(mapload, list/flame_hit) + . = ..() + INVOKE_ASYNC(src, .proc/fall, flame_hit) -/obj/effect/temp_visual/target/proc/fall() +/obj/effect/temp_visual/target/proc/fall(list/flame_hit) var/turf/T = get_turf(src) - playsound(T,'sound/magic/fireball.ogg', 200, 1) + playsound(T,'sound/magic/fleshtostone.ogg', 80, TRUE) new /obj/effect/temp_visual/fireball(T) - sleep(12) - explosion(T, 0, 0, 1, 0, 0, 0, 1, 1) - -/mob/living/simple_animal/hostile/megafauna/dragon/OpenFire() - anger_modifier = Clamp(((maxHealth - health)/50),0,20) - ranged_cooldown = world.time + ranged_cooldown_time - if(swooping) - fire_rain() - return - - if(prob(15 + anger_modifier) && !client) - if(health < maxHealth/2) - spawn(0) - swoop_attack(1) - else - fire_rain() - - else if(prob(10+anger_modifier) && !client) - if(health > maxHealth/2) - spawn(0) - swoop_attack(0) - else - spawn(0) - triple_swoop(0) - else - fire_walls() - -/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_rain() - visible_message("Fire rains from the sky!") - for(var/turf/turf in range(12,get_turf(src))) - if(prob(10)) - new /obj/effect/temp_visual/target(turf) - -/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_walls() - playsound(get_turf(src),'sound/magic/fireball.ogg', 200, 1) - - for(var/d in cardinal) - spawn(0) - fire_wall(d) - -/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_wall(dir) - var/list/hit_things = list(src) - var/turf/E = get_edge_target_turf(src, dir) - var/range = 10 - var/turf/previousturf = get_turf(src) - for(var/turf/J in getline(src,E)) - if(!range || !previousturf.CanAtmosPass(J)) - break - range-- - new /obj/effect/hotspot(J) - J.hotspot_expose(700,50,1) - for(var/mob/living/L in J.contents - hit_things) - L.adjustFireLoss(20) + sleep(duration) + if(ismineralturf(T)) + var/turf/simulated/mineral/M = T + M.gets_drilled() + playsound(T, "explosion", 80, TRUE) + new /obj/effect/hotspot(T) + T.hotspot_expose(700, 50, 1) + for(var/mob/living/L in T.contents) + if(istype(L, /mob/living/simple_animal/hostile/megafauna/dragon)) + continue + if(islist(flame_hit) && !flame_hit[L]) + L.adjustFireLoss(40) to_chat(L, "You're hit by the drake's fire breath!") - hit_things += L - previousturf = J - sleep(1) - -/mob/living/simple_animal/hostile/megafauna/dragon/proc/triple_swoop() - swoop_attack() - swoop_attack() - swoop_attack() - -/mob/living/simple_animal/hostile/megafauna/dragon/proc/swoop_attack(fire_rain = 0, atom/movable/manual_target) - if(stat || swooping) - return - swoop_cooldown = world.time + 200 - var/atom/swoop_target - if(manual_target) - swoop_target = manual_target - else - swoop_target = target - stop_automated_movement = TRUE - swooping = 1 - density = 0 - icon_state = "swoop" - visible_message("[src] swoops up high!") - if(prob(50)) - animate(src, pixel_x = 500, pixel_z = 500, time = 10) - else - animate(src, pixel_x = -500, pixel_z = 500, time = 10) - sleep(30) - - var/turf/tturf - if(fire_rain) - fire_rain() - - icon_state = "dragon" - if(swoop_target && !QDELETED(swoop_target) && swoop_target.z == src.z) - tturf = get_turf(swoop_target) - else - tturf = get_turf(src) - forceMove(tturf) - new /obj/effect/temp_visual/dragon_swoop(tturf) - animate(src, pixel_x = initial(pixel_x), pixel_z = 0, time = 10) - sleep(10) - playsound(src.loc, 'sound/effects/meteorimpact.ogg', 200, 1) - for(var/mob/living/L in orange(1, src)) - if(L.stat) - visible_message("[src] slams down on [L], crushing [L.p_them()]!") - L.gib() + flame_hit[L] = TRUE else - L.adjustBruteLoss(75) - if(L && !QDELETED(L)) // Some mobs are deleted on death - var/throw_dir = get_dir(src, L) - if(L.loc == loc) - throw_dir = pick(alldirs) - var/throwtarget = get_edge_target_turf(src, throw_dir) - L.throw_at(throwtarget, 3) - visible_message("[L] is thrown clear of [src]!") - - for(var/mob/M in range(7, src)) - shake_camera(M, 15, 1) - - stop_automated_movement = FALSE - swooping = 0 - density = 1 - -/mob/living/simple_animal/hostile/megafauna/dragon/AltClickOn(atom/movable/A) - if(!istype(A)) - return - if(swoop_cooldown >= world.time) - to_chat(src, "You need to wait 20 seconds between swoop attacks!") - return - swoop_attack(1, A) - -/obj/item/gps/internal/dragon - icon_state = null - gpstag = "Fiery Signal" - desc = "Here there be dragons." - invisibility = 100 + L.adjustFireLoss(10) //if we've already hit them, do way less damage /mob/living/simple_animal/hostile/megafauna/dragon/lesser name = "lesser ash drake" @@ -267,9 +581,92 @@ Difficulty: Medium obj_damage = 80 melee_damage_upper = 30 melee_damage_lower = 30 + mouse_opacity = MOUSE_OPACITY_ICON damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) loot = list() crusher_loot = list() + butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30) + attack_action_types = list() + +/mob/living/simple_animal/hostile/megafauna/dragon/lesser/AltClickOn(atom/movable/A) + if(!istype(A)) + return + if(player_cooldown >= world.time) + to_chat(src, "You need to wait [(player_cooldown - world.time) / 10] seconds before swooping again!") + return + swoop_attack(FALSE, A) + lava_pools(10, 2) // less pools but longer delay before spawns + player_cooldown = world.time + 200 // needs seperate cooldown or cant use fire attacks /mob/living/simple_animal/hostile/megafauna/dragon/lesser/grant_achievement(medaltype,scoretype) + return + +/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon + name = "space dragon" + maxHealth = 250 + health = 250 + faction = list("neutral") + desc = "A space carp turned dragon by vile magic. Has the same ferocity of a space carp, but also a much more enabling body." + icon = 'icons/mob/spacedragon.dmi' + icon_state = "spacedragon" + icon_living = "spacedragon" + icon_dead = "spacedragon_dead" + obj_damage = 80 + melee_damage_upper = 35 + melee_damage_lower = 35 + speed = 0 + mouse_opacity = MOUSE_OPACITY_ICON + loot = list() + crusher_loot = list() + butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30) + move_force = MOVE_FORCE_NORMAL + move_resist = MOVE_FORCE_NORMAL + pull_force = MOVE_FORCE_NORMAL + deathmessage = "screeches as its wings turn to dust and it collapses on the floor, life estinguished." + attack_action_types = list() + +/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/grant_achievement(medaltype, scoretype) + return + +/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/Initialize(mapload) + var/obj/effect/proc_holder/spell/aoe_turf/repulse/spacedragon/repulse_action = new /obj/effect/proc_holder/spell/aoe_turf/repulse/spacedragon(src) + repulse_action.action.Grant(src) + mob_spell_list += repulse_action + . = ..() + +/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/proc/fire_stream(var/atom/at = target) + playsound(get_turf(src),'sound/magic/fireball.ogg', 200, TRUE) + SLEEP_CHECK_DEATH(0) + var/range = 20 + var/list/turfs = list() + turfs = line_target(0, range, at) + INVOKE_ASYNC(src, .proc/fire_line, turfs) + +/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/OpenFire() + if(swooping) + return + ranged_cooldown = world.time + ranged_cooldown_time + fire_stream() + +/obj/effect/proc_holder/spell/aoe_turf/repulse/spacedragon + name = "Tail Sweep" + desc = "Throw back attackers with a sweep of your tail." + sound = 'sound/magic/tail_swing.ogg' + charge_max = 150 + clothes_req = FALSE + range = 1 + cooldown_min = 150 + invocation_type = "none" + sparkle_path = /obj/effect/temp_visual/dir_setting/tailsweep + action_icon_state = "tailsweep" + action_background_icon_state = "bg_alien" + +/obj/effect/proc_holder/spell/aoe_turf/repulse/spacedragon/cast(list/targets, mob/user = usr) + if(iscarbon(user)) + var/mob/living/carbon/C = user + playsound(C.loc, 'sound/effects/hit_punch.ogg', 80, TRUE, TRUE) + C.spin(6, 1) + ..(targets, user, 3) + +/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/AltClickOn(atom/movable/A) return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 70c713b37eb..088d8631e77 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -2,124 +2,422 @@ The Hierophant -The Hierophant spawns in its arena, an area designed to make it harder to fight than it would otherwise be. +The Hierophant spawns in its arena, which makes fighting it challenging but not impossible. The text this boss speaks is ROT4, use ROT22 to decode -The Hierophant's attacks are as follows, and INTENSIFY at a random chance based on Hierophant's health; -- Creates a cardinal or diagonal blast(Cross Blast) under its target, exploding after a short time. - INTENSITY EFFECT: Creates one of the cross blast types under itself instead of under the target. - INTENSITY EFFECT: The created Cross Blast fires in all directions if below half health. -- If no chasers exist, creates a chaser that will seek its target, leaving a trail of blasts. - INTENSITY EFFECT: Creates a second, slower chaser. -- Creates an expanding AoE burst. -- INTENSE ATTACKS: +The Hierophant's attacks are as follows; +- These attacks happen at a random, increasing chance: If target is at least 2 tiles away; Blinks to the target after a very brief delay, damaging everything near the start and end points. As above, but does so multiple times if below half health. - Rapidly creates Cross Blasts under a target. - If chasers are off cooldown, creates four high-speed chasers. -- IF TARGET WAS STRUCK IN MELEE: Creates a 3x3 square of blasts under the target. + Rapidly creates cardinal and diagonal Cross Blasts under a target. + If chasers are off cooldown, creates 4 chasers. -Cross Blasts and the AoE burst gain additional range as the Hierophant loses health, while Chasers gain additional speed. +- IF TARGET IS OUTSIDE THE ARENA: Creates an arena around the target for 10 seconds, blinking to the target if not in the created arena. + The arena has a 20 second cooldown, giving people a small window to get the fuck out. -When The Hierophant dies, it leaves behind its staff, which, while much weaker than when wielded by The Hierophant itself, is still quite effective. -- The staff can place a teleport rune, allowing the user to teleport themself and their allies to the rune. +- If no chasers exist, creates a chaser that will seek its target, leaving a trail of blasts. + Is more likely to create a second, slower, chaser if hurt. +- If the target is at least 2 tiles away, may Blink to the target after a very brief delay, damaging everything near the start and end points. +- Creates a cardinal or diagonal blast(Cross Blast) under its target, exploding after a short time. + If below half health, the created Cross Blast may fire in all directions. +- Creates an expanding AoE burst. + +- IF ATTACKING IN MELEE: Creates an expanding AoE burst. + +Cross Blasts and the AoE burst gain additional range as Hierophant loses health, while Chasers gain additional speed. + +When Hierophant dies, it stops trying to murder you and shrinks into a small form, which, while much weaker, is still quite effective. +- The smaller club can place a teleport beacon, allowing the user to teleport themself and their allies to the beacon. Difficulty: Hard */ /mob/living/simple_animal/hostile/megafauna/hierophant - name = "Hierophant" - desc = "An ancient, powerful priest-like being wielding a mighty staff." + name = "hierophant" + desc = "A massive metal club that hangs in the air as though waiting. It'll make you dance to its beat." health = 2500 maxHealth = 2500 attacktext = "clubs" - //attack_sound = 'sound/weapons/sonic_jackhammer.ogg' - attack_sound = "swing_hit" + attack_sound = 'sound/weapons/sonic_jackhammer.ogg' icon_state = "hierophant" icon_living = "hierophant" friendly = "stares down" - icon = 'icons/mob/lavaland/hierophant.dmi' + icon = 'icons/mob/lavaland/hierophant_new.dmi' faction = list("boss") //asteroid mobs? get that shit out of my beautiful square house speak_emote = list("preaches") armour_penetration = 50 - melee_damage_lower = 10 - melee_damage_upper = 10 - speed = 1 + melee_damage_lower = 15 + melee_damage_upper = 15 + speed = 10 move_to_delay = 10 - ranged = 1 - pixel_x = -16 + ranged = TRUE ranged_cooldown_time = 40 - aggro_vision_range = 23 - loot = list(/obj/item/hierophant_staff) - crusher_loot = list(/obj/item/hierophant_staff, /obj/item/crusher_trophy/vortex_talisman) + aggro_vision_range = 21 //so it can see to one side of the arena to the other + loot = list(/obj/item/hierophant_club) + crusher_loot = list(/obj/item/hierophant_club, /obj/item/crusher_trophy/vortex_talisman) wander = FALSE - var/burst_range = 3 //range on burst aoe - var/beam_range = 5 //range on cross blast beams - var/chaser_speed = 3 //how fast chasers are currently - var/chaser_cooldown = 101 //base cooldown/cooldown var between spawning chasers - var/major_attack_cooldown = 60 //base cooldown for major attacks - var/blinking = FALSE //if we're doing something that requires us to stand still and not attack - var/obj/effect/hierophant/spawned_rune //the rune we teleport back to - var/timeout_time = 15 //after this many Life() ticks with no target, we return to our rune - var/did_reset = TRUE //if we timed out, returned to our rune, and healed some - //var/list/kill_phrases = list("Wsyvgi sj irivkc xettih. Vitemvmrk...", "Irivkc wsyvgi jsyrh. Vitemvmrk...", "Jyip jsyrh. Egxmzexmrk vitemv gcgpiw...") - //var/list/target_phrases = list("Xevkix psgexih.", "Iriqc jsyrh.", "Eguymvih xevkix.") internal_type = /obj/item/gps/internal/hierophant medal_type = BOSS_MEDAL_HIEROPHANT score_type = HIEROPHANT_SCORE del_on_death = TRUE death_sound = 'sound/magic/repulse.ogg' + attack_action_types = list(/datum/action/innate/megafauna_attack/blink, + /datum/action/innate/megafauna_attack/chaser_swarm, + /datum/action/innate/megafauna_attack/cross_blasts, + /datum/action/innate/megafauna_attack/blink_spam) + + var/burst_range = 3 //range on burst aoe + var/beam_range = 5 //range on cross blast beams + var/chaser_speed = 3 //how fast chasers are currently + var/chaser_cooldown = 101 //base cooldown/cooldown var between spawning chasers + var/major_attack_cooldown = 60 //base cooldown for major attacks + var/arena_cooldown = 200 //base cooldown/cooldown var for creating an arena + var/blinking = FALSE //if we're doing something that requires us to stand still and not attack + var/obj/effect/hierophant/spawned_beacon //the beacon we teleport back to + var/timeout_time = 15 //after this many Life() ticks with no target, we return to our beacon + var/did_reset = TRUE //if we timed out, returned to our beacon, and healed some + var/list/kill_phrases = list("Wsyvgi sj irivkc xettih. Vitemvmrk...", "Irivkc wsyvgi jsyrh. Vitemvmrk...", "Jyip jsyrh. Egxmzexmrk vitemv gcgpiw...", "Kix fiex. Liepmrk...") + var/list/target_phrases = list("Xevkix psgexih.", "Iriqc jsyrh.", "Eguymvih xevkix.") + var/list/stored_nearby = list() // stores people nearby the hierophant when it enters the death animation /mob/living/simple_animal/hostile/megafauna/hierophant/Initialize(mapload) . = ..() - spawned_rune = new(loc) + spawned_beacon = new(loc) -/mob/living/simple_animal/hostile/megafauna/hierophant/Life(seconds, times_fired) +/datum/action/innate/megafauna_attack/blink + name = "Blink To Target" + icon_icon = 'icons/mob/actions/actions.dmi' + button_icon_state = "sniper_zoom" + chosen_message = "You are now blinking to your target." + chosen_attack_num = 1 + +/datum/action/innate/megafauna_attack/chaser_swarm + name = "Chaser Swarm" + icon_icon = 'icons/effects/effects.dmi' + button_icon_state = "hierophant_squares_indefinite" + chosen_message = "You are firing a chaser swarm at your target." + chosen_attack_num = 2 + +/datum/action/innate/megafauna_attack/cross_blasts + name = "Cross Blasts" + icon_icon = 'icons/effects/effects.dmi' + button_icon_state = "hierophant_blast_indefinite" + chosen_message = "You are now firing cross blasts at your target." + chosen_attack_num = 3 + +/datum/action/innate/megafauna_attack/blink_spam + name = "Blink Chase" + icon_icon = 'icons/obj/lavaland/artefacts.dmi' + button_icon_state = "hierophant_club_ready_beacon" + chosen_message = "You are now repeatedly blinking at your target." + chosen_attack_num = 4 + +/mob/living/simple_animal/hostile/megafauna/hierophant/OpenFire() + if(blinking) + return + + calculate_rage() + var/blink_counter = 1 + round(anger_modifier * 0.08) + var/cross_counter = 1 + round(anger_modifier * 0.12) + + arena_trap(target) + ranged_cooldown = world.time + max(5, ranged_cooldown_time - anger_modifier * 0.75) //scale cooldown lower with high anger. + + var/target_slowness = 0 + var/mob/living/L + if(isliving(target)) + L = target + target_slowness += L.movement_delay() + if(client) + target_slowness += 1 + + target_slowness = max(target_slowness, 1) + chaser_speed = max(1, (3 - anger_modifier * 0.04) + ((target_slowness - 1) * 0.5)) + + if(client) + switch(chosen_attack) + if(1) + blink(target) + if(2) + chaser_swarm(blink_counter, target_slowness, cross_counter) + if(3) + cross_blast_spam(blink_counter, target_slowness, cross_counter) + if(4) + blink_spam(blink_counter, target_slowness, cross_counter) + return + + if(L?.stat == DEAD && !blinking && get_dist(src, L) > 2) + blink(L) + return + + if(prob(anger_modifier * 0.75)) //major ranged attack + var/list/possibilities = list() + if(cross_counter > 1) + possibilities += "cross_blast_spam" + if(get_dist(src, target) > 2) + possibilities += "blink_spam" + if(chaser_cooldown < world.time) + if(prob(anger_modifier * 2)) + possibilities = list("chaser_swarm") + else + possibilities += "chaser_swarm" + if(possibilities.len) + switch(pick(possibilities)) + if("blink_spam") //blink either once or multiple times. + blink_spam(blink_counter, target_slowness, cross_counter) + if("cross_blast_spam") //fire a lot of cross blasts at a target. + cross_blast_spam(blink_counter, target_slowness, cross_counter) + if("chaser_swarm") //fire four fucking chasers at a target and their friends. + chaser_swarm(blink_counter, target_slowness, cross_counter) + return + + if(chaser_cooldown < world.time) //if chasers are off cooldown, fire some! + var/obj/effect/temp_visual/hierophant/chaser/C = new /obj/effect/temp_visual/hierophant/chaser(loc, src, target, chaser_speed, FALSE) + chaser_cooldown = world.time + initial(chaser_cooldown) + if((prob(anger_modifier) || target.Adjacent(src)) && target != src) + var/obj/effect/temp_visual/hierophant/chaser/OC = new(loc, src, target, chaser_speed * 1.5, FALSE) + OC.moving = 4 + OC.moving_dir = pick(cardinal - C.moving_dir) + + else if(prob(10 + (anger_modifier * 0.5)) && get_dist(src, target) > 2) + blink(target) + + else if(prob(70 - anger_modifier)) //a cross blast of some type + if(prob(anger_modifier * (2 / target_slowness)) && health < maxHealth * 0.5) //we're super angry do it at all dirs + INVOKE_ASYNC(src, .proc/blasts, target, alldirs) + else if(prob(60)) + INVOKE_ASYNC(src, .proc/blasts, target, cardinal) + else + INVOKE_ASYNC(src, .proc/blasts, target, diagonals) + else //just release a burst of power + INVOKE_ASYNC(src, .proc/burst, get_turf(src)) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blink_spam(var/blink_counter, var/target_slowness, var/cross_counter) + ranged_cooldown = world.time + max(5, major_attack_cooldown - anger_modifier * 0.75) + if(health < maxHealth * 0.5 && blink_counter > 1) + visible_message("\"Mx ampp rsx iwgeti.\"") + var/oldcolor = color + animate(src, color = "#660099", time = 6) + SLEEP_CHECK_DEATH(6) + while(!QDELETED(target) && blink_counter) + if(loc == target.loc || loc == target) //we're on the same tile as them after about a second we can stop now + break + blink_counter-- + blinking = FALSE + blink(target) + blinking = TRUE + SLEEP_CHECK_DEATH(4 + target_slowness) + animate(src, color = oldcolor, time = 8) + addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8) + SLEEP_CHECK_DEATH(8) + blinking = FALSE + else + blink(target) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/cross_blast_spam(var/blink_counter, var/target_slowness, var/cross_counter) + ranged_cooldown = world.time + max(5, major_attack_cooldown - anger_modifier * 0.75) + visible_message("\"Piezi mx rsalivi xs vyr.\"") + blinking = TRUE + var/oldcolor = color + animate(src, color = "#660099", time = 6) + SLEEP_CHECK_DEATH(6) + while(!QDELETED(target) && cross_counter) + cross_counter-- + if(prob(60)) + INVOKE_ASYNC(src, .proc/blasts, target, cardinal) + else + INVOKE_ASYNC(src, .proc/blasts, target, diagonals) + SLEEP_CHECK_DEATH(6 + target_slowness) + animate(src, color = oldcolor, time = 8) + addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8) + SLEEP_CHECK_DEATH(8) + blinking = FALSE + + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/chaser_swarm(var/blink_counter, var/target_slowness, var/cross_counter) + ranged_cooldown = world.time + max(5, major_attack_cooldown - anger_modifier * 0.75) + visible_message("\"Mx gerrsx lmhi.\"") + blinking = TRUE + var/oldcolor = color + animate(src, color = "#660099", time = 6) + SLEEP_CHECK_DEATH(6) + var/list/targets = ListTargets() + var/list/cardinal_copy = cardinal.Copy() + while(targets.len && cardinal_copy.len) + var/mob/living/pickedtarget = pick(targets) + if(targets.len >= cardinal_copy.len) + pickedtarget = pick_n_take(targets) + if(!istype(pickedtarget) || pickedtarget.stat == DEAD) + pickedtarget = target + if(QDELETED(pickedtarget) || (istype(pickedtarget) && pickedtarget.stat == DEAD)) + break //main target is dead and we're out of living targets, cancel out + var/obj/effect/temp_visual/hierophant/chaser/C = new(loc, src, pickedtarget, chaser_speed, FALSE) + C.moving = 3 + C.moving_dir = pick_n_take(cardinal_copy) + SLEEP_CHECK_DEATH(8 + target_slowness) + chaser_cooldown = world.time + initial(chaser_cooldown) + animate(src, color = oldcolor, time = 8) + addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8) + SLEEP_CHECK_DEATH(8) + blinking = FALSE + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blasts(mob/victim, var/list/directions = cardinal) //fires cross blasts with a delay + var/turf/T = get_turf(victim) + if(!T) + return + if(directions == cardinal) + new /obj/effect/temp_visual/hierophant/telegraph/cardinal(T, src) + else if(directions == diagonals) + new /obj/effect/temp_visual/hierophant/telegraph/diagonal(T, src) + else + new /obj/effect/temp_visual/hierophant/telegraph(T, src) + playsound(T,'sound/effects/bin_close.ogg', 200, TRUE) + SLEEP_CHECK_DEATH(2) + new /obj/effect/temp_visual/hierophant/blast(T, src, FALSE) + for(var/d in directions) + INVOKE_ASYNC(src, .proc/blast_wall, T, d) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blast_wall(turf/T, set_dir) //make a wall of blasts beam_range tiles long + var/range = beam_range + var/turf/previousturf = T + var/turf/J = get_step(previousturf, set_dir) + for(var/i in 1 to range) + new /obj/effect/temp_visual/hierophant/blast(J, src, FALSE) + previousturf = J + J = get_step(previousturf, set_dir) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/arena_trap(mob/victim) //trap a target in an arena + var/turf/T = get_turf(victim) + if(!istype(victim) || victim.stat == DEAD || !T || arena_cooldown > world.time) + return + if((istype(get_area(T), /area/ruin/unpowered/hierophant) || istype(get_area(src), /area/ruin/unpowered/hierophant)) && victim != src) + return + arena_cooldown = world.time + initial(arena_cooldown) + for(var/d in cardinal) + INVOKE_ASYNC(src, .proc/arena_squares, T, d) + for(var/t in RANGE_TURFS(11, T)) + if(t && get_dist(t, T) == 11) + new /obj/effect/temp_visual/hierophant/wall(t, src) + new /obj/effect/temp_visual/hierophant/blast(t, src, FALSE) + if(get_dist(src, T) >= 11) //hey you're out of range I need to get closer to you! + INVOKE_ASYNC(src, .proc/blink, T) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/arena_squares(turf/T, set_dir) //make a fancy effect extending from the arena target + var/turf/previousturf = T + var/turf/J = get_step(previousturf, set_dir) + for(var/i in 1 to 10) + var/obj/effect/temp_visual/hierophant/squares/HS = new(J) + HS.setDir(set_dir) + previousturf = J + J = get_step(previousturf, set_dir) + SLEEP_CHECK_DEATH(0.5) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blink(mob/victim) //blink to a target + if(blinking || !victim) + return + var/turf/T = get_turf(victim) + var/turf/source = get_turf(src) + new /obj/effect/temp_visual/hierophant/telegraph(T, src) + new /obj/effect/temp_visual/hierophant/telegraph(source, src) + playsound(T,'sound/magic/wand_teleport.ogg', 200, TRUE) + playsound(source,'sound/machines/airlock_open.ogg', 200, TRUE) + blinking = TRUE + SLEEP_CHECK_DEATH(2) //short delay before we start... + new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, src) + new /obj/effect/temp_visual/hierophant/telegraph/teleport(source, src) + for(var/t in RANGE_TURFS(1, T)) + var/obj/effect/temp_visual/hierophant/blast/B = new(t, src, FALSE) + B.damage = 30 + for(var/t in RANGE_TURFS(1, source)) + var/obj/effect/temp_visual/hierophant/blast/B = new(t, src, FALSE) + B.damage = 30 + animate(src, alpha = 0, time = 2, easing = EASE_OUT) //fade out + SLEEP_CHECK_DEATH(1) + visible_message("[src] fades out!") + density = FALSE + SLEEP_CHECK_DEATH(2) + forceMove(T) + SLEEP_CHECK_DEATH(1) + animate(src, alpha = 255, time = 2, easing = EASE_IN) //fade IN + SLEEP_CHECK_DEATH(1) + density = TRUE + visible_message("[src] fades in!") + SLEEP_CHECK_DEATH(1) //at this point the blasts we made detonate + blinking = FALSE + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/melee_blast(mob/victim) //make a 3x3 blast around a target + if(!victim) + return + var/turf/T = get_turf(victim) + if(!T) + return + new /obj/effect/temp_visual/hierophant/telegraph(T, src) + playsound(T,'sound/effects/bin_close.ogg', 200, TRUE) + SLEEP_CHECK_DEATH(2) + for(var/t in RANGE_TURFS(1, T)) + new /obj/effect/temp_visual/hierophant/blast(t, src, FALSE) + +//expanding square +/proc/hierophant_burst(mob/caster, turf/original, burst_range, spread_speed = 0.5) + playsound(original,'sound/machines/airlock_open.ogg', 200, TRUE) + var/last_dist = 0 + for(var/t in spiral_range_turfs(burst_range, original)) + var/turf/T = t + if(!T) + continue + var/dist = get_dist(original, T) + if(dist > last_dist) + last_dist = dist + sleep(1 + min(burst_range - last_dist, 12) * spread_speed) //gets faster as it gets further out + new /obj/effect/temp_visual/hierophant/blast(T, caster, FALSE) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/burst(turf/original, spread_speed) + hierophant_burst(src, original, burst_range, spread_speed) + +/mob/living/simple_animal/hostile/megafauna/hierophant/Life() . = ..() - if(. && spawned_rune && !client) - if(target || loc == spawned_rune.loc) + if(. && spawned_beacon && !QDELETED(spawned_beacon) && !client) + if(target || loc == spawned_beacon.loc) timeout_time = initial(timeout_time) else timeout_time-- if(timeout_time <= 0 && !did_reset) did_reset = TRUE - //visible_message("\"Vixyvrmrk xs fewi...\"") - blink(spawned_rune) - adjustHealth(min((health - maxHealth) * 0.5, -50)) //heal for 50% of our missing health + visible_message("\"Vixyvrmrk xs fewi...\"") + blink(spawned_beacon) + adjustHealth(min((health - maxHealth) * 0.5, -250)) //heal for 50% of our missing health, minimum 10% of maximum health wander = FALSE - /*if(health > maxHealth * 0.9) + if(health > maxHealth * 0.9) visible_message("\"Vitemvw gsqtpixi. Stivexmrk ex qebmqyq ijjmgmirgc.\"") else - visible_message("\"Vitemvw gsqtpixi. Stivexmsrep ijjmgmirgc gsqtvsqmwih.\"")*/ + visible_message("\"Vitemvw gsqtpixi. Stivexmsrep ijjmgmirgc gsqtvsqmwih.\"") /mob/living/simple_animal/hostile/megafauna/hierophant/death() - if(!can_die()) - return FALSE - blinking = TRUE //we do a fancy animation, release a huge burst(), and leave our staff. - animate(src, alpha = 0, color = "660099", time = 20, easing = EASE_OUT) - burst_range = 10 - //visible_message("\"Mrmxmexmrk wipj-hiwxvygx wiuyirgi...\"") - visible_message("[src] disappears in a massive burst of magic, leaving only its staff.") - burst(get_turf(src)) - // Things are in this order due to `del_on_death` - return ..() + if(health > 0 || stat == DEAD) + return + else + stat = DEAD + blinking = TRUE //we do a fancy animation, release a huge burst(), and leave our staff. + visible_message("\"Mrmxmexmrk wipj-hiwxvygx wiuyirgi...\"") + visible_message("[src] shrinks, releasing a massive burst of energy!") + for(var/mob/living/L in view(7, src)) + stored_nearby += L // store the people to grant the achievements to once we die + hierophant_burst(null, get_turf(src), 10) + stat = CONSCIOUS // deathgasp wont run if dead, stupid + ..(/* force_grant = stored_nearby */) /mob/living/simple_animal/hostile/megafauna/hierophant/Destroy() - QDEL_NULL(spawned_rune) + QDEL_NULL(spawned_beacon) return ..() /mob/living/simple_animal/hostile/megafauna/hierophant/devour(mob/living/L) for(var/obj/item/W in L) if(!L.unEquip(W)) qdel(W) - /*visible_message( - "\"[pick(kill_phrases)]\"\n[src] annihilates [L]!", - "You annihilate [L], restoring your health!")*/ - visible_message( - "\"Caw.\"\n[src] annihilates [L]!", - "You annihilate [L], restoring your health!") + visible_message("\"[pick(kill_phrases)]\"") + visible_message("[src] annihilates [L]!","You annihilate [L], restoring your health!") adjustHealth(-L.maxHealth*0.5) L.dust() @@ -128,24 +426,36 @@ Difficulty: Hard if(istype(the_target, /mob/living/simple_animal/hostile/asteroid/hivelordbrood)) //ignore temporary targets in favor of more permanent targets return FALSE -/*/mob/living/simple_animal/hostile/megafauna/hierophant/GiveTarget(new_target) +/mob/living/simple_animal/hostile/megafauna/hierophant/GiveTarget(new_target) var/targets_the_same = (new_target == target) . = ..() if(. && target && !targets_the_same) - visible_message("\"[pick(target_phrases)]\"")*/ + visible_message("\"[pick(target_phrases)]\"") + if(spawned_beacon && loc == spawned_beacon.loc && did_reset) + arena_trap(src) /mob/living/simple_animal/hostile/megafauna/hierophant/adjustHealth(amount, updating_health = TRUE) . = ..() - if(src && amount > 0 && !blinking) + if(src && . && !blinking) wander = TRUE did_reset = FALSE /mob/living/simple_animal/hostile/megafauna/hierophant/AttackingTarget() if(!blinking) if(target && isliving(target)) - spawn(0) - melee_blast(get_turf(target)) //melee attacks on living mobs produce a 3x3 blast - return ..() + var/mob/living/L = target + if(L.stat != DEAD) + if(ranged_cooldown <= world.time) + calculate_rage() + ranged_cooldown = world.time + max(5, ranged_cooldown_time - anger_modifier * 0.75) + INVOKE_ASYNC(src, .proc/burst, get_turf(src)) + else + burst_range = 3 + INVOKE_ASYNC(src, .proc/burst, get_turf(src), 0.25) //melee attacks on living mobs cause it to release a fast burst if on cooldown + else + devour(L) + else + return ..() /mob/living/simple_animal/hostile/megafauna/hierophant/DestroySurroundings() if(!blinking) @@ -153,9 +463,16 @@ Difficulty: Hard /mob/living/simple_animal/hostile/megafauna/hierophant/Move() if(!blinking) - /*if(!stat) - playsound(loc, 'sound/mecha/mechmove04.ogg', 150, 1, -4)*/ - ..() + . = ..() + +/mob/living/simple_animal/hostile/megafauna/hierophant/Moved(oldLoc, movement_dir) + . = ..() + if(!stat && .) + var/obj/effect/temp_visual/hierophant/squares/HS = new(oldLoc) + HS.setDir(movement_dir) + playsound(src, 'sound/mecha/mechmove04.ogg', 150, TRUE, -4) + if(target) + arena_trap(target) /mob/living/simple_animal/hostile/megafauna/hierophant/Goto(target, delay, minimum_distance) wander = TRUE @@ -168,254 +485,59 @@ Difficulty: Hard burst_range = initial(burst_range) + round(anger_modifier * 0.08) beam_range = initial(beam_range) + round(anger_modifier * 0.12) -/mob/living/simple_animal/hostile/megafauna/hierophant/OpenFire() - calculate_rage() - var/target_is_slow = FALSE - if(isliving(target)) - var/mob/living/L = target - if(!blinking && L.stat == DEAD && get_dist(src, L) > 2) - blink(L) - return - if(L.movement_delay() > 1.5) - target_is_slow = TRUE - chaser_speed = max(1, (3 - anger_modifier * 0.04) + target_is_slow * 0.5) - if(blinking) - return - ranged_cooldown = world.time + max(5, ranged_cooldown_time - anger_modifier * 0.75) //scale cooldown lower with high anger. - - if(prob(anger_modifier * 0.75)) //major ranged attack - var/list/possibilities = list() - var/cross_counter = 1 + round(anger_modifier * 0.12) - if(cross_counter > 1) - possibilities += "cross_blast_spam" - if(get_dist(src, target) > 2) - possibilities += "blink_spam" - if(chaser_cooldown < world.time) - if(prob(anger_modifier * 2)) - possibilities = list("chaser_swarm") - else - possibilities += "chaser_swarm" - if(possibilities.len) - ranged_cooldown = world.time + max(5, major_attack_cooldown - anger_modifier * 0.75) //we didn't cancel out of an attack, use the higher cooldown - var/blink_counter = 1 + round(anger_modifier * 0.08) - switch(pick(possibilities)) - if("blink_spam") //blink either once or multiple times. - if(health < maxHealth * 0.5 && !target_is_slow && blink_counter > 1) - //visible_message("\"Mx ampp rsx iwgeti.\"") - animate(src, color = "#660099", time = 6) - while(health && target && blink_counter) - if(loc == target.loc || loc == target) //we're on the same tile as them after about a second we can stop now - break - blink_counter-- - blinking = FALSE - blink(target) - blinking = TRUE - sleep(5) - animate(src, color = initial(color), time = 8) - sleep(8) - blinking = FALSE - else - blink(target) - if("cross_blast_spam") //fire a lot of cross blasts at a target. - //visible_message("\"Piezi mx rsalivi xs vyr.\"") - blinking = TRUE - animate(src, color = "#660099", time = 6) - while(health && target && cross_counter) - cross_counter-- - var/delay = 6 - if(prob(60)) - spawn(0) - cardinal_blasts(target) - else - spawn(0) - diagonal_blasts(target) - delay = 5 //this one isn't so mean, so do the next one faster(if there is one) - sleep(delay) - animate(src, color = initial(color), time = 8) - sleep(8) - blinking = FALSE - if("chaser_swarm") //fire four fucking chasers at a target and their friends. - //visible_message("\"Mx gerrsx lmhi.\"") - blinking = TRUE - animate(src, color = "#660099", time = 10) - var/list/targets = ListTargets() - var/list/cardinal_copy = cardinal.Copy() - while(health && targets.len && cardinal_copy.len) - var/mob/living/pickedtarget = pick(targets) - if(targets.len > 4) - pickedtarget = pick_n_take(targets) - if(pickedtarget.stat == DEAD) - pickedtarget = target - var/obj/effect/temp_visual/hierophant/chaser/C = new /obj/effect/temp_visual/hierophant/chaser(loc, src, pickedtarget, chaser_speed, FALSE) - C.moving = 3 - C.moving_dir = pick_n_take(cardinal_copy) - sleep(10) - chaser_cooldown = world.time + initial(chaser_cooldown) - animate(src, color = initial(color), time = 8) - sleep(8) - blinking = FALSE - return - - if(prob(10 + (anger_modifier * 0.5)) && get_dist(src, target) > 2) - blink(target) - - else if(prob(70 - anger_modifier)) //a cross blast of some type - if(prob(anger_modifier)) //at us? - if(prob(anger_modifier * 2) && health < maxHealth * 0.5) //we're super angry do it at all dirs - spawn(0) - alldir_blasts(src) - else if(prob(60)) - spawn(0) - cardinal_blasts(src) - else - spawn(0) - diagonal_blasts(src) - else //at them? - if(prob(anger_modifier * 2) && health < maxHealth * 0.5 && !target_is_slow) //we're super angry do it at all dirs - spawn(0) - alldir_blasts(target) - else if(prob(60)) - spawn(0) - cardinal_blasts(target) - else - spawn(0) - diagonal_blasts(target) - else if(chaser_cooldown < world.time) //if chasers are off cooldown, fire some! - var/obj/effect/temp_visual/hierophant/chaser/C = new /obj/effect/temp_visual/hierophant/chaser(loc, src, target, chaser_speed, FALSE) - chaser_cooldown = world.time + initial(chaser_cooldown) - if((prob(anger_modifier) || target.Adjacent(src)) && target != src) - var/obj/effect/temp_visual/hierophant/chaser/OC = new /obj/effect/temp_visual/hierophant/chaser(loc, src, target, max(1.5, 5 - anger_modifier * 0.07), FALSE) - OC.moving = 4 - OC.moving_dir = pick(cardinal - C.moving_dir) - else //just release a burst of power - spawn(0) - burst(get_turf(src)) - -/mob/living/simple_animal/hostile/megafauna/hierophant/proc/diagonal_blasts(mob/victim) //fire diagonal cross blasts with a delay - var/turf/T = get_turf(victim) - if(!T) - return - new /obj/effect/temp_visual/hierophant/telegraph/diagonal(T, src) - playsound(T,'sound/magic/blink.ogg', 200, 1) - //playsound(T,'sound/effects/bin_close.ogg', 200, 1) - sleep(2) - new /obj/effect/temp_visual/hierophant/blast(T, src, FALSE) - for(var/d in diagonals) - spawn(0) - blast_wall(T, d) - -/mob/living/simple_animal/hostile/megafauna/hierophant/proc/cardinal_blasts(mob/victim) //fire cardinal cross blasts with a delay - var/turf/T = get_turf(victim) - if(!T) - return - new /obj/effect/temp_visual/hierophant/telegraph/cardinal(T, src) - playsound(T,'sound/magic/blink.ogg', 200, 1) - //playsound(T,'sound/effects/bin_close.ogg', 200, 1) - sleep(2) - new /obj/effect/temp_visual/hierophant/blast(T, src, FALSE) - for(var/d in cardinal) - spawn(0) - blast_wall(T, d) - -/mob/living/simple_animal/hostile/megafauna/hierophant/proc/alldir_blasts(mob/victim) //fire alldir cross blasts with a delay - var/turf/T = get_turf(victim) - if(!T) - return - new /obj/effect/temp_visual/hierophant/telegraph(T, src) - playsound(T,'sound/magic/blink.ogg', 200, 1) - //playsound(T,'sound/effects/bin_close.ogg', 200, 1) - sleep(2) - new /obj/effect/temp_visual/hierophant/blast(T, src, FALSE) - for(var/d in alldirs) - spawn(0) - blast_wall(T, d) - -/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blast_wall(turf/T, dir) //make a wall of blasts beam_range tiles long - var/range = beam_range - var/turf/previousturf = T - var/turf/J = get_step(previousturf, dir) - for(var/i in 1 to range) - new /obj/effect/temp_visual/hierophant/blast(J, src, FALSE) - previousturf = J - J = get_step(previousturf, dir) - -/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blink(mob/victim) //blink to a target - if(blinking || !victim) - return - var/turf/T = get_turf(victim) - var/turf/source = get_turf(src) - new /obj/effect/temp_visual/hierophant/telegraph(T, src) - new /obj/effect/temp_visual/hierophant/telegraph(source, src) - playsound(T,'sound/magic/blink.ogg', 200, 1) - //playsound(T,'sound/magic/wand_teleport.ogg', 200, 1) - playsound(source,'sound/magic/blink.ogg', 200, 1) - //playsound(source,'sound/machines/AirlockOpen.ogg', 200, 1) - blinking = TRUE - sleep(2) //short delay before we start... - new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, src) - new /obj/effect/temp_visual/hierophant/telegraph/teleport(source, src) - for(var/t in RANGE_TURFS(1, T)) - var/obj/effect/temp_visual/hierophant/blast/B = new /obj/effect/temp_visual/hierophant/blast(t, src, FALSE) - B.damage = 30 - for(var/t in RANGE_TURFS(1, source)) - var/obj/effect/temp_visual/hierophant/blast/B = new /obj/effect/temp_visual/hierophant/blast(t, src, FALSE) - B.damage = 30 - animate(src, alpha = 0, time = 2, easing = EASE_OUT) //fade out - sleep(1) - visible_message("[src] fades out!") - density = FALSE - sleep(2) - forceMove(T) - sleep(1) - animate(src, alpha = 255, time = 2, easing = EASE_IN) //fade IN - sleep(1) - density = TRUE - visible_message("[src] fades in!") - sleep(1) //at this point the blasts we made detonate - blinking = FALSE - -/mob/living/simple_animal/hostile/megafauna/hierophant/proc/melee_blast(mob/victim) //make a 3x3 blast around a target - if(!victim) - return - var/turf/T = get_turf(victim) - if(!T) - return - new /obj/effect/temp_visual/hierophant/telegraph(T, src) - playsound(T,'sound/magic/blink.ogg', 200, 1) - //playsound(T,'sound/effects/bin_close.ogg', 200, 1) - sleep(2) - for(var/t in RANGE_TURFS(1, T)) - new /obj/effect/temp_visual/hierophant/blast(t, src, FALSE) - -/mob/living/simple_animal/hostile/megafauna/hierophant/proc/burst(turf/original) //release a wave of blasts - playsound(original,'sound/magic/blink.ogg', 200, 1) - //playsound(original,'sound/machines/AirlockOpen.ogg', 200, 1) - var/last_dist = 0 - for(var/t in spiral_range_turfs(burst_range, original)) - var/turf/T = t - if(!T) - continue - var/dist = get_dist(original, T) - if(dist > last_dist) - last_dist = dist - sleep(1 + (burst_range - last_dist) * 0.5) //gets faster as it gets further out - new /obj/effect/temp_visual/hierophant/blast(T, src, FALSE) - -/mob/living/simple_animal/hostile/megafauna/hierophant/AltClickOn(atom/A) //player control handler(don't give this to a player holy fuck) - if(!istype(A) || get_dist(A, src) <= 2) - return - blink(A) - //Hierophant overlays /obj/effect/temp_visual/hierophant - layer = MOB_LAYER - 0.1 + name = "vortex energy" + layer = BELOW_MOB_LAYER var/mob/living/caster //who made this, anyway -/obj/effect/temp_visual/hierophant/New(loc, new_caster) - ..() +/obj/effect/temp_visual/hierophant/Initialize(mapload, new_caster) + . = ..() if(new_caster) caster = new_caster +/obj/effect/temp_visual/hierophant/squares + icon_state = "hierophant_squares" + duration = 3 + light_range = MINIMUM_USEFUL_LIGHT_RANGE + randomdir = FALSE + +/obj/effect/temp_visual/hierophant/squares/Initialize(mapload, new_caster) + . = ..() + if(ismineralturf(loc)) + var/turf/simulated/mineral/M = loc + M.gets_drilled(caster) + +/obj/effect/temp_visual/hierophant/wall //smoothing and pooling were not friends, but pooling is dead. + name = "vortex wall" + icon = 'icons/turf/walls/hierophant_wall_temp.dmi' + icon_state = "wall" + light_range = MINIMUM_USEFUL_LIGHT_RANGE + duration = 100 + smooth = SMOOTH_TRUE + +/obj/effect/temp_visual/hierophant/wall/Initialize(mapload, new_caster) + . = ..() + queue_smooth_neighbors(src) + queue_smooth(src) + +/obj/effect/temp_visual/hierophant/wall/Destroy() + queue_smooth_neighbors(src) + return ..() + +/obj/effect/temp_visual/hierophant/wall/CanPass(atom/movable/mover, turf/target) + if(QDELETED(caster)) + return FALSE + if(mover == caster.pulledby) + return TRUE + if(istype(mover, /obj/item/projectile)) + var/obj/item/projectile/P = mover + if(P.firer == caster) + return TRUE + if(mover == caster) + return TRUE + return FALSE + /obj/effect/temp_visual/hierophant/chaser //a hierophant's chaser. follows target around, moving and producing a blast every speed deciseconds. duration = 98 var/mob/living/target //what it's following @@ -429,15 +551,16 @@ Difficulty: Hard var/speed = 3 //how many deciseconds between each step var/currently_seeking = FALSE var/friendly_fire_check = FALSE //if blasts produced apply friendly fire + var/monster_damage_boost = TRUE + var/damage = 10 -/obj/effect/temp_visual/hierophant/chaser/New(loc, new_caster, new_target, new_speed, is_friendly_fire) - ..() +/obj/effect/temp_visual/hierophant/chaser/Initialize(mapload, new_caster, new_target, new_speed, is_friendly_fire) + . = ..() target = new_target friendly_fire_check = is_friendly_fire if(new_speed) speed = new_speed - spawn(0) - seek_target() + addtimer(CALLBACK(src, .proc/seek_target), 1) /obj/effect/temp_visual/hierophant/chaser/proc/get_target_dir() . = get_cardinal_dir(src, targetturf) @@ -475,7 +598,9 @@ Difficulty: Hard targetturf = get_turf(target) /obj/effect/temp_visual/hierophant/chaser/proc/make_blast() - new /obj/effect/temp_visual/hierophant/blast(loc, caster, friendly_fire_check) + var/obj/effect/temp_visual/hierophant/blast/B = new(loc, caster, friendly_fire_check) + B.damage = damage + B.monster_damage_boost = monster_damage_boost /obj/effect/temp_visual/hierophant/telegraph icon = 'icons/effects/96x96.dmi' @@ -499,132 +624,117 @@ Difficulty: Hard duration = 40 /obj/effect/temp_visual/hierophant/blast - icon = 'icons/effects/effects.dmi' icon_state = "hierophant_blast" name = "vortex blast" - layer = 3.9 // between LYING_MOB_LAYER and ABOVE_MOB_LAYER - luminosity = 1 + light_range = 2 + light_power = 2 desc = "Get out of the way!" duration = 9 var/damage = 10 //how much damage do we do? + var/monster_damage_boost = TRUE //do we deal extra damage to monsters? Used by the boss var/list/hit_things = list() //we hit these already, ignore them var/friendly_fire_check = FALSE var/bursting = FALSE //if we're bursting and need to hit anyone crossing us -/obj/effect/temp_visual/hierophant/blast/New(loc, new_caster, friendly_fire) - ..() +/obj/effect/temp_visual/hierophant/blast/Initialize(mapload, new_caster, friendly_fire) + . = ..() friendly_fire_check = friendly_fire if(new_caster) hit_things += new_caster if(ismineralturf(loc)) //drill mineral turfs var/turf/simulated/mineral/M = loc M.gets_drilled(caster) - spawn(0) - blast() + INVOKE_ASYNC(src, .proc/blast) /obj/effect/temp_visual/hierophant/blast/proc/blast() var/turf/T = get_turf(src) if(!T) return - playsound(T,'sound/magic/blind.ogg', 125, 1, -5) //make a sound + playsound(T,'sound/magic/blind.ogg', 125, TRUE, -5) //make a sound sleep(6) //wait a little bursting = TRUE do_damage(T) //do damage and mark us as bursting sleep(1.3) //slightly forgiving; the burst animation is 1.5 deciseconds bursting = FALSE //we no longer damage crossers -/obj/effect/temp_visual/hierophant/blast/Crossed(atom/movable/AM, oldloc) +/obj/effect/temp_visual/hierophant/blast/Crossed(atom/movable/AM) ..() if(bursting) do_damage(get_turf(src)) /obj/effect/temp_visual/hierophant/blast/proc/do_damage(turf/T) + if(!damage) + return for(var/mob/living/L in T.contents - hit_things) //find and damage mobs... hit_things += L if((friendly_fire_check && caster && caster.faction_check_mob(L)) || L.stat == DEAD) continue if(L.client) flash_color(L.client, "#660099", 1) - playsound(L,'sound/weapons/sear.ogg', 50, 1, -4) + playsound(L,'sound/weapons/sear.ogg', 50, TRUE, -4) to_chat(L, "You're struck by a [name]!") - var/limb_to_hit = pick("head", "chest", "r_arm", "l_arm", "r_leg", "l_leg") + var/limb_to_hit = L.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)) var/armor = L.run_armor_check(limb_to_hit, "melee", "Your armor absorbs [src]!", "Your armor blocks part of [src]!", 50, "Your armor was penetrated by [src]!") L.apply_damage(damage, BURN, limb_to_hit, armor) - if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) + if(ishostile(L)) + var/mob/living/simple_animal/hostile/H = L //mobs find and damage you... + if(H.stat == CONSCIOUS && !H.target && H.AIStatus != AI_OFF && !H.client) + if(!QDELETED(caster)) + if(get_dist(H, caster) <= H.aggro_vision_range) + H.FindTarget(list(caster), 1) + else + H.Goto(get_turf(caster), H.move_to_delay, 3) + if(monster_damage_boost && (ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid))) L.adjustBruteLoss(damage) - add_attack_logs(caster, L, "Struck with a [name]") - for(var/obj/mecha/M in T.contents - hit_things) //and mechs. + if(caster) + add_attack_logs(caster, L, "Struck with a [name]") + for(var/obj/mecha/M in T.contents - hit_things) //also damage mechs. hit_things += M if(M.occupant) if(friendly_fire_check && caster && caster.faction_check_mob(M.occupant)) continue to_chat(M.occupant, "Your [M.name] is struck by a [name]!") - playsound(M,'sound/weapons/sear.ogg', 50, 1, -4) + playsound(M,'sound/weapons/sear.ogg', 50, TRUE, -4) M.take_damage(damage, BURN, 0, 0) -/obj/effect/temp_visual/hierophant/wall //smoothing and pooling were not friends, but pooling is dead. - name = "vortex wall" - icon = 'icons/turf/walls/hierophant_wall_temp.dmi' - icon_state = "wall" - light_range = MINIMUM_USEFUL_LIGHT_RANGE - duration = 100 - smooth = SMOOTH_TRUE - -/obj/effect/temp_visual/hierophant/wall/New(loc, new_caster) - ..() - queue_smooth_neighbors(src) - queue_smooth(src) - -/obj/effect/temp_visual/hierophant/wall/Destroy() - queue_smooth_neighbors(src) - return ..() - -/obj/effect/temp_visual/hierophant/wall/CanPass(atom/movable/mover, turf/target) - if(QDELETED(caster)) - return FALSE - if(mover == caster.pulledby) - return TRUE - if(istype(mover, /obj/item/projectile)) - var/obj/item/projectile/P = mover - if(P.firer == caster) - return TRUE - if(mover == caster) - return TRUE - return FALSE - - /obj/effect/hierophant - name = "hierophant rune" - desc = "A powerful magic mark allowing whomever attunes themself to it to return to it at will." - icon = 'icons/obj/rune.dmi' - icon_state = "hierophant" - layer = 2.5 + name = "hierophant beacon" + desc = "A strange beacon, allowing mass teleportation for those able to use it." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "hierophant_tele_off" + light_range = 2 + layer = LOW_OBJ_LAYER anchored = TRUE - color = "#CC00FF" + +/obj/effect/hierophant/ex_act() + return /obj/effect/hierophant/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/hierophant_staff)) - var/obj/item/hierophant_staff/H = I - if(H.rune == src) - to_chat(user, "You start removing your hierophant rune...") + if(istype(I, /obj/item/hierophant_club)) + var/obj/item/hierophant_club/H = I + if(H.timer > world.time) + return + if(H.beacon == src) + to_chat(user, "You start removing your hierophant beacon...") H.timer = world.time + 51 + INVOKE_ASYNC(H, /obj/item/hierophant_club.proc/prepare_icon_update) if(do_after(user, 50, target = src)) - playsound(src,'sound/magic/blind.ogg', 200, 1, -4) + playsound(src,'sound/magic/blind.ogg', 200, TRUE, -4) new /obj/effect/temp_visual/hierophant/telegraph/teleport(get_turf(src), user) - to_chat(user, "You touch the rune with the staff, dispelling it!") - H.rune = null + to_chat(user, "You collect [src], reattaching it to the club!") + H.beacon = null user.update_action_buttons_icon() qdel(src) else H.timer = world.time + INVOKE_ASYNC(H, /obj/item/hierophant_club.proc/prepare_icon_update) else - to_chat(user, "You touch the rune with the staff, but nothing happens.") - + to_chat(user, "You touch the beacon with the club, but nothing happens.") else - ..() + return ..() /obj/item/gps/internal/hierophant icon_state = null gpstag = "Zealous Signal" desc = "Heed its words." - invisibility = 100 + invisibility = 100 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index 130cfb3f847..81c96135b0e 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -7,11 +7,11 @@ sentience_type = SENTIENCE_BOSS environment_smash = ENVIRONMENT_SMASH_RWALLS obj_damage = 400 - luminosity = 3 + light_range = 3 faction = list("mining", "boss") weather_immunities = list("lava","ash") - flying = 1 - robust_searching = 1 + flying = TRUE + robust_searching = TRUE ranged_ignores_vision = TRUE stat_attack = DEAD atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) @@ -20,6 +20,12 @@ maxbodytemp = INFINITY vision_range = 5 aggro_vision_range = 18 + move_force = MOVE_FORCE_OVERPOWERING + move_resist = MOVE_FORCE_OVERPOWERING + pull_force = MOVE_FORCE_OVERPOWERING + mob_size = MOB_SIZE_LARGE + layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise + mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway var/list/crusher_loot var/medal_type var/score_type = BOSS_SCORE @@ -27,13 +33,9 @@ var/anger_modifier = 0 var/obj/item/gps/internal_gps var/internal_type - move_force = MOVE_FORCE_OVERPOWERING - move_resist = MOVE_FORCE_OVERPOWERING - pull_force = MOVE_FORCE_OVERPOWERING - mob_size = MOB_SIZE_LARGE - layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise - mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway + var/recovery_time = 0 var/true_spawn = TRUE // if this is a megafauna that should grant achievements, or have a gps signal + var/nest_range = 10 var/chosen_attack = 1 // chosen attack num var/list/attack_action_types = list() @@ -50,6 +52,16 @@ QDEL_NULL(internal_gps) return ..() +/mob/living/simple_animal/hostile/megafauna/Moved() + if(nest && nest.parent && get_dist(nest.parent, src) > nest_range) + var/turf/closest = get_turf(nest.parent) + for(var/i = 1 to nest_range) + closest = get_step(closest, get_dir(closest, src)) + forceMove(closest) // someone teleported out probably and the megafauna kept chasing them + target = null + return + return ..() + /mob/living/simple_animal/hostile/megafauna/can_die() return ..() && health <= 0 @@ -68,6 +80,8 @@ loot = crusher_loot /mob/living/simple_animal/hostile/megafauna/AttackingTarget() + if(recovery_time >= world.time) + return . = ..() if(. && isliving(target)) var/mob/living/L = target @@ -90,13 +104,14 @@ /mob/living/simple_animal/hostile/megafauna/proc/devour(mob/living/L) if(!L) - return + return FALSE visible_message( "[src] devours [L]!", "You feast on [L], restoring your health!") - if(!is_station_level(z) && !client) //NPC monsters won't heal while on station + if(!is_station_level(z) || client) //NPC monsters won't heal while on station adjustBruteLoss(-L.maxHealth/2) L.gib() + return TRUE /mob/living/simple_animal/hostile/megafauna/ex_act(severity, target) switch(severity) @@ -109,6 +124,10 @@ if(3) adjustBruteLoss(50) +/mob/living/simple_animal/hostile/megafauna/proc/SetRecoveryTime(buffer_time) + recovery_time = world.time + buffer_time + ranged_cooldown = world.time + buffer_time + /mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill) if(!medal_type || admin_spawned || !SSmedals.hub_enabled) //Don't award medals if the medal type isn't set return FALSE diff --git a/code/modules/reagents/chemistry/reagents/food.dm b/code/modules/reagents/chemistry/reagents/food.dm index 46d2e1f4db3..4dcc8f15a42 100644 --- a/code/modules/reagents/chemistry/reagents/food.dm +++ b/code/modules/reagents/chemistry/reagents/food.dm @@ -336,6 +336,31 @@ M.bodytemperature = min(310, M.bodytemperature + (5 * TEMPERATURE_DAMAGE_COEFFICIENT)) return ..() +/datum/reagent/consumable/garlic + name = "Garlic Juice" + id = "garlic" + description = "Crushed garlic. Chefs love it, but it can make you smell bad." + color = "#FEFEFE" + taste_description = "garlic" + metabolization_rate = 0.15 * REAGENTS_METABOLISM + +/datum/reagent/consumable/garlic/on_mob_life(mob/living/carbon/M) + var/update_flags = STATUS_UPDATE_NONE + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.mind && H.mind.vampire && !H.mind.vampire.get_ability(/datum/vampire_passive/full)) //incapacitating but not lethal. + if(prob(min(25, current_cycle))) + to_chat(H, "You can't get the scent of garlic out of your nose! You can barely think...") + H.Weaken(1) + H.Jitter(10) + H.fakevomit() + else + if(H.job == "Chef") + if(prob(20)) //stays in the system much longer than sprinkles/banana juice, so heals slower to partially compensate + update_flags |= H.adjustBruteLoss(-1, FALSE) + update_flags |= H.adjustFireLoss(-1, FALSE) + return ..() | update_flags + /datum/reagent/consumable/sprinkles name = "Sprinkles" id = "sprinkles" @@ -941,7 +966,7 @@ id = "entpoly" description = "An ichor, derived from a certain mushroom, makes for a bad time." color = "#1d043d" - taste_description = "mold" + taste_description = "bitter mushroom" /datum/reagent/consumable/entpoly/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE @@ -960,13 +985,13 @@ id = "tinlux" description = "A stimulating ichor which causes luminescent fungi to grow on the skin. " color = "#b5a213" - var/light_activated = 0 - taste_description = "mold" + var/light_activated = FALSE + taste_description = "tingling mushroom" /datum/reagent/consumable/tinlux/on_mob_life(mob/living/M) if(!light_activated) M.set_light(2) - light_activated = 1 + light_activated = TRUE return ..() /datum/reagent/consumable/tinlux/on_mob_delete(mob/living/M) @@ -978,11 +1003,11 @@ description = "A bubbly paste that heals wounds of the skin." color = "#d3a308" nutriment_factor = 3 * REAGENTS_METABOLISM - taste_description = "sweetness" + taste_description = "fruity mushroom" /datum/reagent/consumable/vitfro/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE if(prob(80)) update_flags |= M.adjustBruteLoss(-1 * REAGENTS_EFFECT_MULTIPLIER, FALSE) update_flags |= M.adjustFireLoss(-1 * REAGENTS_EFFECT_MULTIPLIER, FALSE) - return ..() | update_flags + return ..() | update_flags \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 3abfbfd0e19..c4e54c20626 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -82,12 +82,12 @@ /obj/item/reagent_containers/hypospray/combat name = "combat stimulant injector" desc = "A modified air-needle autoinjector, used by support operatives to quickly heal injuries in combat." - amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(10) + amount_per_transfer_from_this = 15 + possible_transfer_amounts = list(15) icon_state = "combat_hypo" - volume = 75 + volume = 90 ignore_flags = 1 // So they can heal their comrades. - list_reagents = list("epinephrine" = 30, "omnizine" = 30, "teporone" = 15) + list_reagents = list("epinephrine" = 30, "weak_omnizine" = 30, "salglu_solution" = 30) /obj/item/reagent_containers/hypospray/combat/nanites desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with expensive medical nanites for rapid healing." diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm index 486111821f1..1784cf4741e 100644 --- a/code/modules/reagents/reagent_containers/patch.dm +++ b/code/modules/reagents/reagent_containers/patch.dm @@ -28,8 +28,8 @@ list_reagents = list("silver_sulfadiazine" = 40) /obj/item/reagent_containers/food/pill/patch/synthflesh - name = "syntheflesh patch" - desc = "Helps with burn injuries." + name = "synthflesh patch" + desc = "Helps with brute and burn injuries." icon_state = "bandaid_med" instant_application = 1 list_reagents = list("synthflesh" = 20) diff --git a/code/modules/surgery/organs/helpers.dm b/code/modules/surgery/organs/helpers.dm index fb1167edb01..91bba8d4a1b 100644 --- a/code/modules/surgery/organs/helpers.dm +++ b/code/modules/surgery/organs/helpers.dm @@ -10,6 +10,9 @@ /mob/proc/get_int_organ_tag(tag) //is it a brain, is it a brain_tumor? return +/mob/living/proc/get_organ(zone) + return + /mob/living/carbon/get_int_organ(typepath) return (locate(typepath) in internal_organs) diff --git a/config/example/config.txt b/config/example/config.txt index 183f85437f0..7dfdc1e413a 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -439,6 +439,8 @@ HIGH_POP_MC_MODE_AMOUNT 65 ##Disengage high pop mode if player count drops below this DISABLE_HIGH_POP_MC_MODE_AMOUNT 60 +##Developer options + ##Uncomment to enable developer start. Auto starts the server after initialization ##DEVELOPER_EXPRESS_START @@ -447,3 +449,6 @@ DISABLE_HIGH_POP_MC_MODE_AMOUNT 60 ## Uncomment to give a confirmation before hitting start now #START_NOW_CONFIRMATION + +## If uncommented, all gamemodes will respect the number of required players. Defaults to no. +#ENABLE_GAMEMODE_PLAYER_LIMIT \ No newline at end of file diff --git a/goon/browserassets/css/browserOutput-dark.css b/goon/browserassets/css/browserOutput-dark.css index 426493dfc88..c3d7915c818 100644 --- a/goon/browserassets/css/browserOutput-dark.css +++ b/goon/browserassets/css/browserOutput-dark.css @@ -404,8 +404,8 @@ h1.alert, h2.alert {color: #FFF;} /* MEGAFAUNA */ .colossus {color: #7F282A; font-size: 175%;} -.hierophant {color: #660099; font-weight: bold; font-size: 150%;} -.hierophant_warning {color: #660099; font-weight: bold; font-style: italic;} +.hierophant {color: #660099; font-weight: bold; font-style: italic;} +.hierophant_warning {color: #660099; font-style: italic;} /* EMOJI STUFF */ .emoji {max-height: 16px; max-width: 16px} diff --git a/goon/browserassets/css/browserOutput.css b/goon/browserassets/css/browserOutput.css index 07ae914de62..62dd8b2714b 100644 --- a/goon/browserassets/css/browserOutput.css +++ b/goon/browserassets/css/browserOutput.css @@ -401,8 +401,8 @@ h1.alert, h2.alert {color: #000000;} /* MEGAFAUNA */ .colossus {color: #7F282A; font-size: 175%;} -.hierophant {color: #660099; font-weight: bold; font-size: 150%;} -.hierophant_warning {color: #660099; font-weight: bold; font-style: italic;} +.hierophant {color: #660099; font-weight: bold; font-style: italic;} +.hierophant_warning {color: #660099; font-style: italic;} /* EMOJI STUFF */ .emoji {max-height: 16px; max-width: 16px} diff --git a/html/changelogs/AutoChangeLog-pr-12237.yml b/html/changelogs/AutoChangeLog-pr-12237.yml new file mode 100644 index 00000000000..024cc25e5f7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12237.yml @@ -0,0 +1,4 @@ +author: "variableundefined" +delete-after: True +changes: + - rscadd: "Config option to let gamemode ignore the number of required players." diff --git a/html/changelogs/AutoChangeLog-pr-12264.yml b/html/changelogs/AutoChangeLog-pr-12264.yml new file mode 100644 index 00000000000..141b5320f95 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12264.yml @@ -0,0 +1,4 @@ +author: "variableundefined" +delete-after: True +changes: + - tweak: "Contribution guideline has been updated. No in game changes." diff --git a/html/changelogs/AutoChangeLog-pr-12266.yml b/html/changelogs/AutoChangeLog-pr-12266.yml new file mode 100644 index 00000000000..2c5e69cd06a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12266.yml @@ -0,0 +1,9 @@ +author: "Fox McCloud" +delete-after: True +changes: + - tweak: "Updates moonflower, sunflower, novaflower, and pineapple sprites" + - rscadd: "Adds in Garlic" + - bugfix: "Fixes a few issues with incorrect values from the wine PR" + - rscadd: "Can now grow and extract the reagents/traits of Lavaland flora" + - tweak: "Slightly tweaks soybean oil production on soybeans" + - rscadd: "Adds amusing garlic suicide" diff --git a/html/changelogs/AutoChangeLog-pr-12269.yml b/html/changelogs/AutoChangeLog-pr-12269.yml new file mode 100644 index 00000000000..224e4bb250c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12269.yml @@ -0,0 +1,4 @@ +author: "variableundefined" +delete-after: True +changes: + - tweak: "Github pull request template has been changed" diff --git a/html/changelogs/AutoChangeLog-pr-12270.yml b/html/changelogs/AutoChangeLog-pr-12270.yml new file mode 100644 index 00000000000..13d9f8b9af5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12270.yml @@ -0,0 +1,8 @@ +author: "Fox McCloud" +delete-after: True +changes: + - rscadd: "Ash Drake reworked; it's much more powerful now and has a diverse set of attacks--its fire breath is more random, it moves faster, it can turn the ground into lava, and once it's lower on health, will seal you into an arena and make you play the \"only one turf is safe; stand on it or suffer\" mini-game" + - rscadd: "Bubblegum is now much more difficult; instead of just spraying blood at you and attempting to endlessly charge you (walking backwards essentially counters it), it's now faster, engages in zig-zag attacks, uses a hallucination multi-attack, and can rapidly melee attack you in range" + - bugfix: "Fixes megafauana endlessly attacking dead mobs" + - tweak: "Blood drunk Colossus, and Ash Drake made to be more player friendly if they control the fauna" + - rscadd: "Adds in Space dragon; a very weak version of the Ash drake which can't swoop, but can still breathe fire--has a spell that whips you back with its tail, too (not currently used anywhere)" diff --git a/html/changelogs/AutoChangeLog-pr-12271.yml b/html/changelogs/AutoChangeLog-pr-12271.yml new file mode 100644 index 00000000000..6c262112390 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12271.yml @@ -0,0 +1,9 @@ +author: "AzuleUtama" +delete-after: True +changes: + - rscdel: "Sarin gas grenades no longer purchasable for nuke ops. +balance: Grenadier belt now contains a sarin gas grenade. +balance: Tactical medkit's hypospray tweaked to contain chems better for dealing with newcrit. +balance: C20 bundle increased in price to 18TC, contains an additional clip of ammo. +balance: Medical bundle increased in price to 20TC, Donk LMG removed, replaced with medbeam gun and magboots." + - spellcheck: "Fixed some minor spelling/grammar errors with synthflesh patches and sarin gas grenades." diff --git a/html/changelogs/AutoChangeLog-pr-12277.yml b/html/changelogs/AutoChangeLog-pr-12277.yml new file mode 100644 index 00000000000..addc61219e9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12277.yml @@ -0,0 +1,4 @@ +author: "Fox McCloud" +delete-after: True +changes: + - tweak: "The real Roman shield and real Roman helmets in the Autodrobe have been replaced by fake, armorless, varieties" diff --git a/html/changelogs/AutoChangeLog-pr-12278.yml b/html/changelogs/AutoChangeLog-pr-12278.yml new file mode 100644 index 00000000000..242c925c3e4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12278.yml @@ -0,0 +1,4 @@ +author: "Shadow-Quill" +delete-after: True +changes: + - tweak: "Blobs now get a audio notification roundstart (like traitors do.)" diff --git a/html/changelogs/AutoChangeLog-pr-12281.yml b/html/changelogs/AutoChangeLog-pr-12281.yml new file mode 100644 index 00000000000..f5c2cfb926c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12281.yml @@ -0,0 +1,4 @@ +author: "Dave-TH" +delete-after: True +changes: + - bugfix: "Fixes some vending machines (namely miner nanomeds) from adding all of their product twice." diff --git a/html/changelogs/AutoChangeLog-pr-12282.yml b/html/changelogs/AutoChangeLog-pr-12282.yml new file mode 100644 index 00000000000..7126179431b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12282.yml @@ -0,0 +1,4 @@ +author: "Couls" +delete-after: True +changes: + - bugfix: "hotkey menu option in the dropdown works correctly now" diff --git a/html/changelogs/AutoChangeLog-pr-12285.yml b/html/changelogs/AutoChangeLog-pr-12285.yml new file mode 100644 index 00000000000..952df794846 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12285.yml @@ -0,0 +1,4 @@ +author: "TDSSS" +delete-after: True +changes: + - tweak: "lowered attack log level of self-harm" diff --git a/html/changelogs/AutoChangeLog-pr-12287.yml b/html/changelogs/AutoChangeLog-pr-12287.yml new file mode 100644 index 00000000000..ea664bfe534 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12287.yml @@ -0,0 +1,4 @@ +author: "AzuleUtama" +delete-after: True +changes: + - bugfix: "R&D Console no longer has a default design called 'Name' show up in the mining category." diff --git a/html/changelogs/AutoChangeLog-pr-12289.yml b/html/changelogs/AutoChangeLog-pr-12289.yml new file mode 100644 index 00000000000..3f7723d5aed --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12289.yml @@ -0,0 +1,4 @@ +author: "Ty-Omaha" +delete-after: True +changes: + - bugfix: "SSD players no longer drown in shallow water" diff --git a/html/changelogs/AutoChangeLog-pr-12291.yml b/html/changelogs/AutoChangeLog-pr-12291.yml new file mode 100644 index 00000000000..71c9a090ebb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12291.yml @@ -0,0 +1,6 @@ +author: "Fox McCloud" +delete-after: True +changes: + - rscadd: "Hierophant has been updated--it'll be a bit tougher and will attempt to trap you if you run for it." + - tweak: "Heirophant is no longer a weird bird thing, but a massive, indecipherable piece of machinery that will make you dance to ITS tune" + - tweak: "Hierophant club is weaker up front, but becomes stronger the lower health you are" diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index bea6f281f7a..7e1bb227ef3 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi index 66795e14830..4ea648dea13 100644 Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi index 36025c083b4..2e3c9f7cae0 100644 Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ diff --git a/icons/mob/inhands/64x64_lefthand.dmi b/icons/mob/inhands/64x64_lefthand.dmi index 69b9922f140..f0b5ec6f0e7 100644 Binary files a/icons/mob/inhands/64x64_lefthand.dmi and b/icons/mob/inhands/64x64_lefthand.dmi differ diff --git a/icons/mob/inhands/64x64_righthand.dmi b/icons/mob/inhands/64x64_righthand.dmi index 09cef64e2b0..36f930193d2 100644 Binary files a/icons/mob/inhands/64x64_righthand.dmi and b/icons/mob/inhands/64x64_righthand.dmi differ diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index 7d0961fc449..3a0c32e68b4 100644 Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index 5850e4d8d8c..6cf43537d88 100644 Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ diff --git a/icons/mob/lavaland/hierophant_new.dmi b/icons/mob/lavaland/hierophant_new.dmi new file mode 100644 index 00000000000..84348700324 Binary files /dev/null and b/icons/mob/lavaland/hierophant_new.dmi differ diff --git a/icons/mob/spacedragon.dmi b/icons/mob/spacedragon.dmi new file mode 100644 index 00000000000..98f20ea8851 Binary files /dev/null and b/icons/mob/spacedragon.dmi differ diff --git a/icons/obj/hydroponics/growing.dmi b/icons/obj/hydroponics/growing.dmi index d66cdc2c0dd..438d764cd11 100644 Binary files a/icons/obj/hydroponics/growing.dmi and b/icons/obj/hydroponics/growing.dmi differ diff --git a/icons/obj/hydroponics/growing_flowers.dmi b/icons/obj/hydroponics/growing_flowers.dmi index cceb7491212..e0476c80fe8 100644 Binary files a/icons/obj/hydroponics/growing_flowers.dmi and b/icons/obj/hydroponics/growing_flowers.dmi differ diff --git a/icons/obj/hydroponics/growing_fruits.dmi b/icons/obj/hydroponics/growing_fruits.dmi index 8259c77f570..90583ad14cb 100644 Binary files a/icons/obj/hydroponics/growing_fruits.dmi and b/icons/obj/hydroponics/growing_fruits.dmi differ diff --git a/icons/obj/hydroponics/growing_mushrooms.dmi b/icons/obj/hydroponics/growing_mushrooms.dmi index 7a0eedbdae7..20633cf85b3 100644 Binary files a/icons/obj/hydroponics/growing_mushrooms.dmi and b/icons/obj/hydroponics/growing_mushrooms.dmi differ diff --git a/icons/obj/hydroponics/growing_vegetables.dmi b/icons/obj/hydroponics/growing_vegetables.dmi index 9fc1520175e..c13ce2d5210 100644 Binary files a/icons/obj/hydroponics/growing_vegetables.dmi and b/icons/obj/hydroponics/growing_vegetables.dmi differ diff --git a/icons/obj/hydroponics/harvest.dmi b/icons/obj/hydroponics/harvest.dmi index 3059eabe629..2259813e20c 100644 Binary files a/icons/obj/hydroponics/harvest.dmi and b/icons/obj/hydroponics/harvest.dmi differ diff --git a/icons/obj/hydroponics/seeds.dmi b/icons/obj/hydroponics/seeds.dmi index 353d2811e67..fd35ff9a2bd 100644 Binary files a/icons/obj/hydroponics/seeds.dmi and b/icons/obj/hydroponics/seeds.dmi differ diff --git a/icons/obj/lavaland/artefacts.dmi b/icons/obj/lavaland/artefacts.dmi index 8e91b20368f..bc95bbc1fe3 100644 Binary files a/icons/obj/lavaland/artefacts.dmi and b/icons/obj/lavaland/artefacts.dmi differ diff --git a/icons/obj/lavaland/ash_flora.dmi b/icons/obj/lavaland/ash_flora.dmi index d58541612d2..c8c128a0ab5 100644 Binary files a/icons/obj/lavaland/ash_flora.dmi and b/icons/obj/lavaland/ash_flora.dmi differ diff --git a/icons/turf/floors/hierophant_floor.dmi b/icons/turf/floors/hierophant_floor.dmi new file mode 100644 index 00000000000..365f887bfa4 Binary files /dev/null and b/icons/turf/floors/hierophant_floor.dmi differ diff --git a/icons/turf/walls/hierophant_wall.dmi b/icons/turf/walls/hierophant_wall.dmi index c51880ddef2..8963db4e996 100644 Binary files a/icons/turf/walls/hierophant_wall.dmi and b/icons/turf/walls/hierophant_wall.dmi differ diff --git a/interface/skin.dmf b/interface/skin.dmf index a9af84c81ed..5bca7b5de8b 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -125,7 +125,7 @@ menu "menu" saved-params = "is-checked" elem name = "&Hotkeys" - command = "hotkeys-help" + command = "Hotkey-Help" category = "&Help" saved-params = "is-checked" diff --git a/paradise.dme b/paradise.dme index 18012618db5..740c4715447 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1600,6 +1600,7 @@ #include "code\modules\hydroponics\grown\cotton.dm" #include "code\modules\hydroponics\grown\eggplant.dm" #include "code\modules\hydroponics\grown\flowers.dm" +#include "code\modules\hydroponics\grown\garlic.dm" #include "code\modules\hydroponics\grown\grass_carpet.dm" #include "code\modules\hydroponics\grown\herbals.dm" #include "code\modules\hydroponics\grown\kudzu.dm"