diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index fb33f2fafb..38a16bdba9 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -53,7 +53,8 @@ Mostly for chomp exclusive stuff, otherwise if you need to modify a base file fo * For multi-line removals: Use a block comment (/\* xxx \*/) to comment out the existing code block (do not modify whitespace more than necessary) and at the start, it should contain /\* CHOMP Removal - "Reason" * If it is something like a bugfix that Polaris or Vorestation would want (the codebase we use), you may want to consider coding it there as well. They may want any general gameplay bugfixes, and things that are obviously intended to work one way, but do not. They do not have any of our fluff species (vulp, akula, fenn, etc) so do not make PRs related to that, or any vore content to them. * Change whitespace as little as possible. Do not randomly add/remove whitespace. -* Any new files should have "_ch" at the end. For example, "life_ch.dm". Just make them in the same location as the file they are related to. +* Any new files should preferrably go into the modular_chomp folder following the file structure of where it would be placed normally. The old method was to have "_ch" at the end. For example, "life_ch.dm". +* Do not make changes to base icon files. New icon files should go into modular_chomp and code should be changed to point to the new file. * Map changes must be in tgm format. See the [Mapmerge2 Readme] for details, or use [StrongDMM] which can automatically save maps as tgm. The `attempt_ch()` proc has been added for your convienence. It allows a many-line change to become a single-line change in the existing Polaris files, preserving mergeability and allowing better code separation while preventing your new code from causing runtimes that stop the original code from running. If you are wanting to inject new procedures into an existing proc, called `update_atoms()` for example, you would create `update_atoms_ch()` in a nearby `_ch.dm` file, and then call to it from a single line in the original `update_atoms()` with `attempt_ch()`. diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index 1ae4237a0d..06fb819b07 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -400,10 +400,6 @@ ///called when removing a given item from a mob, from mob/living/carbon/remove_embedded_object(mob/living/carbon/target, /obj/item) #define COMSIG_CARBON_EMBED_REMOVAL "item_embed_remove_safe" -// /mob/living/simple_animal/hostile signals -#define COMSIG_HOSTILE_ATTACKINGTARGET "hostile_attackingtarget" - #define COMPONENT_HOSTILE_NO_ATTACK (1<<0) - // /obj signals ///from base of obj/deconstruct(): (disassembled) diff --git a/code/controllers/subsystems/chat.dm b/code/controllers/subsystems/chat.dm index aece61d39e..92a1be2757 100644 --- a/code/controllers/subsystems/chat.dm +++ b/code/controllers/subsystems/chat.dm @@ -52,7 +52,7 @@ SUBSYSTEM_DEF(chat) for(var/I in target) var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible - if(!C) + if(!C || !C.chatOutput) continue // No client? No care. else if(C.chatOutput.broken) DIRECT_OUTPUT(C, original_message) @@ -65,7 +65,7 @@ SUBSYSTEM_DEF(chat) else var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible - if(!C) + if(!C || !C.chatOutput) return // No client? No care. else if(C.chatOutput.broken) DIRECT_OUTPUT(C, original_message) diff --git a/code/datums/supplypacks/science_vr.dm b/code/datums/supplypacks/science_vr.dm index 1d9922e7d8..2e9f756aa2 100644 --- a/code/datums/supplypacks/science_vr.dm +++ b/code/datums/supplypacks/science_vr.dm @@ -29,7 +29,7 @@ containertype = /obj/structure/largecrate/animal/weretiger containername = "Weretiger crate" access = access_xenobiology -/* + /datum/supply_pack/sci/otie name = "VARMAcorp adoptable reject (Dangerous!)" cost = 100 @@ -43,4 +43,3 @@ containertype = /obj/structure/largecrate/animal/otie/phoron containername = "VARMAcorp adaptive beta subject (Experimental)" access = access_xenobiology -*/ //VORESTATION AI TEMPORARY REMOVAL. Oties commented out cuz broke. diff --git a/code/datums/supplypacks/security_vr.dm b/code/datums/supplypacks/security_vr.dm index a00556e528..bbb28e08fb 100644 --- a/code/datums/supplypacks/security_vr.dm +++ b/code/datums/supplypacks/security_vr.dm @@ -1,4 +1,4 @@ -/*/datum/supply_pack/security/guardbeast //VORESTATION AI TEMPORARY REMOVAL +/datum/supply_pack/security/guardbeast name = "VARMAcorp autoNOMous security solution" cost = 150 containertype = /obj/structure/largecrate/animal/guardbeast @@ -17,7 +17,6 @@ access_security, access_xenobiology) one_access = TRUE -*/ /datum/supply_pack/randomised/security/armor access = access_armory diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 31d1d07f2b..e8f9e2fe13 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -465,7 +465,7 @@ var/list/mob/living/forced_ambiance_list = new /area/proc/prison_break(break_lights = TRUE, open_doors = TRUE, open_blast_doors = FALSE) //CHOMP Edit set blast doors to FALSE var/obj/machinery/power/apc/theAPC = get_apc() - if(theAPC.operating) + if(theAPC && theAPC.operating) if(break_lights) for(var/obj/machinery/power/apc/temp_apc in src) temp_apc.overload_lighting(70) diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm index 931b298091..d5cdc700c7 100644 --- a/code/game/dna/dna2_helpers.dm +++ b/code/game/dna/dna2_helpers.dm @@ -22,7 +22,7 @@ // Give Random Bad Mutation to M /proc/randmutb(var/mob/living/M) - if(!M) return + if(!M || !(M.dna)) return M.dna.check_integrity() //var/block = pick(GLASSESBLOCK,COUGHBLOCK,FAKEBLOCK,NERVOUSBLOCK,CLUMSYBLOCK,TWITCHBLOCK,HEADACHEBLOCK,BLINDBLOCK,DEAFBLOCK,HALLUCINATIONBLOCK) // Most of these are disabled anyway. var/block = pick(FAKEBLOCK,CLUMSYBLOCK,BLINDBLOCK,DEAFBLOCK) @@ -30,7 +30,7 @@ // Give Random Good Mutation to M /proc/randmutg(var/mob/living/M) - if(!M) return + if(!M || !(M.dna)) return M.dna.check_integrity() //var/block = pick(HULKBLOCK,XRAYBLOCK,FIREBLOCK,TELEBLOCK,NOBREATHBLOCK,REMOTEVIEWBLOCK,REGENERATEBLOCK,INCREASERUNBLOCK,REMOTETALKBLOCK,MORPHBLOCK,BLENDBLOCK,NOPRINTSBLOCK,SHOCKIMMUNITYBLOCK,SMALLSIZEBLOCK) // Much like above, most of these blocks are disabled in code. var/block = pick(HULKBLOCK,XRAYBLOCK,FIREBLOCK,TELEBLOCK,REGENERATEBLOCK,REMOTETALKBLOCK) @@ -38,13 +38,13 @@ // Random Appearance Mutation /proc/randmuti(var/mob/living/M) - if(!M) return + if(!M || !(M.dna)) return M.dna.check_integrity() M.dna.SetUIValue(rand(1,DNA_UI_LENGTH),rand(1,4095)) // Scramble UI or SE. /proc/scramble(var/UI, var/mob/M, var/prob) - if(!M) return + if(!M || !(M.dna)) return M.dna.check_integrity() if(UI) for(var/i = 1, i <= DNA_UI_LENGTH-1, i++) diff --git a/code/game/jobs/job/captain_vr.dm b/code/game/jobs/job/captain_vr.dm index 15a5342deb..62b0eb93ef 100644 --- a/code/game/jobs/job/captain_vr.dm +++ b/code/game/jobs/job/captain_vr.dm @@ -32,13 +32,13 @@ access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_hop, access_RC_announce, access_clown, access_tomfoolery, access_mime, access_keycard_auth, access_gateway) + access_hop, access_RC_announce, access_clown, access_tomfoolery, access_mime, access_keycard_auth, access_gateway, access_entertainment) minimal_access = list(access_security, access_sec_doors, access_brig, access_forensics_lockers, access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads, access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_hop, access_RC_announce, access_clown, access_tomfoolery, access_mime, access_keycard_auth, access_gateway) + access_hop, access_RC_announce, access_clown, access_tomfoolery, access_mime, access_keycard_auth, access_gateway, access_entertainment) /datum/alt_title/deputy_director title = "Deputy Director" diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm index 95784c156b..63f8a702e0 100644 --- a/code/game/jobs/job_controller.dm +++ b/code/game/jobs/job_controller.dm @@ -393,7 +393,7 @@ var/global/datum/controller/occupations/job_master //Equip custom gear loadout. var/list/custom_equip_slots = list() var/list/custom_equip_leftovers = list() - if(H.client.prefs.gear && H.client.prefs.gear.len && !(job.mob_type & JOB_SILICON)) + if(H.client && H.client.prefs && H.client.prefs.gear && H.client.prefs.gear.len && !(job.mob_type & JOB_SILICON)) for(var/thing in H.client.prefs.gear) var/datum/gear/G = gear_datums[thing] if(!G) //Not a real gear datum (maybe removed, as this is loaded from their savefile) diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index b785a42639..18765fcf9b 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -423,6 +423,8 @@ /obj/machinery/sleeper/relaymove(var/mob/user) ..() + if(user.incapacitated()) + return go_out() /obj/machinery/sleeper/emp_act(var/severity) diff --git a/code/game/machinery/deployable_vr.dm b/code/game/machinery/deployable_vr.dm index bbb835b763..c939fea3f4 100644 --- a/code/game/machinery/deployable_vr.dm +++ b/code/game/machinery/deployable_vr.dm @@ -72,7 +72,7 @@ /obj/structure/barricade/cutout/attackby(var/obj/I, var/mob/user) if(is_type_in_list(I, painters)) var/choice = tgui_input_list(user, "What would you like to paint the cutout as?", "Cutout Painting", cutout_types) - if(!choice || !Adjacent(user, src) || I != user.get_active_hand()) + if(!choice || !Adjacent(user) || I != user.get_active_hand()) return TRUE if(do_after(user, 10 SECONDS, src)) var/picked_type = cutout_types[choice] diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index d2adbad8da..703d174bee 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -100,7 +100,7 @@ // Also recharge their internal battery. if(H.isSynthetic() && H.nutrition < 500) //VOREStation Edit - H.nutrition = min(H.nutrition+10, 500) //VOREStation Edit + H.nutrition = min(H.nutrition+(10*(1-H.species.synthetic_food_coeff)), 500) //VOREStation Edit cell.use(7000/450*10) // And clear up radiation diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index 505ff94947..dc0a70f711 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -92,8 +92,8 @@ return -/obj/machinery/teleport/station/attack_ai() - attack_hand() +/obj/machinery/teleport/station/attack_ai(mob/user) + attack_hand(user) /obj/machinery/computer/teleporter/attack_ai(mob/user) teleport_control.tgui_interact(user) diff --git a/code/game/machinery/virtual_reality/vr_console.dm b/code/game/machinery/virtual_reality/vr_console.dm index 9d165cc7e3..81df6d934b 100644 --- a/code/game/machinery/virtual_reality/vr_console.dm +++ b/code/game/machinery/virtual_reality/vr_console.dm @@ -2,9 +2,9 @@ name = "virtual reality sleeper" desc = "A fancy bed with built-in sensory I/O ports and connectors to interface users' minds with their bodies in virtual reality." icon = 'icons/obj/Cryogenic2.dmi' - icon_state = "syndipod_0" + icon_state = "body_scanner_0" - var/base_state = "syndipod_" + var/base_state = "body_scanner_" density = TRUE anchored = TRUE @@ -88,9 +88,9 @@ -/obj/machinery/sleeper/relaymove(var/mob/user) +/obj/machinery/vr_sleeper/relaymove(var/mob/user) ..() - if(usr.incapacitated()) + if(user.incapacitated()) return go_out() @@ -245,9 +245,14 @@ if(occupant.species.name != "Promethean" && occupant.species.name != "Human" && mirror_first_occupant) avatar.shapeshifter_change_shape(occupant.species.name) avatar.forceMove(get_turf(S)) // Put the mob on the landmark, instead of inside it - avatar.Sleeping(1) + occupant.enter_vr(avatar) + //Yes, I am using a aheal just so your markings transfer over, I could not get .prefs.copy_to working. This is very stupid, and I can't be assed to rewrite this. Too bad! + avatar.revive() + avatar.revive() + avatar.verbs += /mob/living/carbon/human/proc/exit_vr //ahealing removes the prommie verbs and the VR verbs, giving it back + avatar.Sleeping(1) // Prompt for username after they've enterred the body. var/newname = sanitize(tgui_input_text(avatar, "You are entering virtual reality. Your username is currently [src.name]. Would you like to change it to something else?", "Name change", null, MAX_NAME_LEN), MAX_NAME_LEN) diff --git a/code/game/objects/banners.dm b/code/game/objects/banners.dm index 0e4aaef6d8..d491e41119 100644 --- a/code/game/objects/banners.dm +++ b/code/game/objects/banners.dm @@ -31,6 +31,12 @@ desc = "A banner with the symbol of the Solar Confederate Government." catalogue_data = list(/datum/category_item/catalogue/information/organization/solgov) +/obj/item/weapon/banner/altevian + name = "\improper Altevian Hegemony Banner" + icon_state = "banner-altevian" + desc = "A banner that flies for the pride of the hegemony." + //catalogue_data = list(/datum/category_item/catalogue/information/organization/altevian_hegemony) // TODO? + //VOREStation Removal //CHOMP re-addition. Seriously? You commented this out for your lore? What's wrong with JUST NOT SPAWNING IT or something. /obj/item/weapon/banner/virgov name = "\improper VirGov banner" diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 2ed58f88ee..645168b041 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -106,7 +106,7 @@ var/drop_sound = "generic_drop" var/tip_timer // reference to timer id for a tooltip we might open soon - + var/no_random_knockdown = FALSE //stops item from being able to randomly knock people down in combat /obj/item/Initialize(mapload) //CHOMPedit I stg I'm going to overwrite these many uncommented edits. @@ -222,9 +222,12 @@ /obj/item/attack_hand(mob/living/user as mob) if (!user) return - if(anchored) - to_chat(user, span("notice", "\The [src] won't budge, you can't pick it up!")) - return + if(anchored) // Start CHOMPStation Edit + if(hascall(src, "attack_self")) + return src.attack_self(user) + else + to_chat ("This is anchored and you can't lift it.") + return // End CHOMPStation Edit if (hasorgans(user)) var/mob/living/carbon/human/H = user var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm index f672cbcf1b..75fddde40e 100644 --- a/code/game/objects/items/devices/defib.dm +++ b/code/game/objects/items/devices/defib.dm @@ -308,8 +308,8 @@ return bad_vital_organ //this needs to be last since if any of the 'other conditions are met their messages take precedence - if(!H.client && !H.teleop) - return "buzzes, \"Resuscitation failed - Mental interface error. Further attempts may be successful.\"" + //if(!H.client && !H.teleop) + // return "buzzes, \"Resuscitation failed - Mental interface error. Further attempts may be successful.\""// CHOMPEdit, removing this check to allow revival through bad internet connections. return null diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 6ae95bcf8a..54cc46d802 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -53,6 +53,7 @@ var/emagged = 0 var/failmsg = "" var/charge = 0 + var/selected_color = LIGHT_COLOR_INCANDESCENT_TUBE //Default color! // Eating used bulbs gives us bulb shards var/bulb_shards = 0 @@ -141,6 +142,10 @@ return */ to_chat(usr, "It has [uses] lights remaining.") + var/new_color = input(usr, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color) as color|null + if(new_color) + selected_color = new_color + to_chat(usr, "The light color has been changed.") /obj/item/device/lightreplacer/update_icon() icon_state = "lightreplacer[emagged]" @@ -184,19 +189,13 @@ to_chat(U, "\The [src] has fabricated a new bulb from the broken bulbs it has stored. It now has [uses] uses.") playsound(src, 'sound/machines/ding.ogg', 50, 1) target.status = LIGHT_EMPTY + target.installed_light = null //Remove the light! target.update() var/obj/item/weapon/light/L2 = new target.light_type() - - target.status = L2.status - target.switchcount = L2.switchcount - target.rigged = emagged - target.brightness_range = L2.brightness_range - target.brightness_power = L2.brightness_power - target.brightness_color = L2.brightness_color - target.on = target.has_power() + L2.brightness_color = selected_color + target.insert_bulb(L2) //Call the insertion proc. target.update() - qdel(L2) if(target.on && target.rigged) target.explode() diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 9676919792..1cb0b442d9 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -20,7 +20,7 @@ to_chat(user, "\The [src] cannot be applied to [M]!") return 1 - if (!M.IsAdvancedToolUser()) + if (!user.IsAdvancedToolUser()) to_chat(user, "You don't have the dexterity to do this!") return 1 diff --git a/code/game/objects/items/toys/toys_vr.dm b/code/game/objects/items/toys/toys_vr.dm index 386d73aaa9..d114b6e4ab 100644 --- a/code/game/objects/items/toys/toys_vr.dm +++ b/code/game/objects/items/toys/toys_vr.dm @@ -1084,4 +1084,27 @@ T = get_turf(src) new /obj/effect/decal/cleanable/confetti(T) else - to_chat(user, "The [src] is already spent!") \ No newline at end of file + to_chat(user, "The [src] is already spent!") + +/* + * Snow Globes + */ +/obj/item/weapon/toy/snowglobe + name = "snowglobe" + icon = 'icons/obj/snowglobe_vr.dmi' + +/obj/item/weapon/toy/snowglobe/snowvillage + desc = "Depicts a small, quaint village buried in snow." + icon_state = "smolsnowvillage" + +/obj/item/weapon/toy/snowglobe/tether + desc = "Depicts a massive space elevator reaching to the sky." + icon_state = "smoltether" + +/obj/item/weapon/toy/snowglobe/stellardelight + desc = "Depicts an interstellar spacecraft." + icon_state = "smolstellardelight" + +/obj/item/weapon/toy/snowglobe/rascalspass + desc = "Depicts a nanotrasen facility on a temperate world." + icon_state = "smolrascalspass" diff --git a/code/game/objects/items/trash_vr.dm b/code/game/objects/items/trash_vr.dm index bb18141f4f..1f6cf5d3a8 100644 --- a/code/game/objects/items/trash_vr.dm +++ b/code/game/objects/items/trash_vr.dm @@ -44,4 +44,19 @@ /obj/item/trash/ratjuice name = "\improper Space-Safe Meal package" icon = 'icons/obj/trash_vr.dmi' - icon_state = "altevian_juice-trash" \ No newline at end of file + icon_state = "altevian_juice-trash" + +/obj/item/trash/ratfruitcake + name = "\improper Flavor Unit package" + icon = 'icons/obj/trash_vr.dmi' + icon_state = "altevian_fruitcake-trash" + +/obj/item/trash/ratpackburger + name = "\improper Prepackaged Meal Tray" + icon = 'icons/obj/trash_vr.dmi' + icon_state = "altevian_pack_burger-trash" + +/obj/item/trash/ratpackcheese + name = "\improper Prepackaged Meal Tray" + icon = 'icons/obj/trash_vr.dmi' + icon_state = "altevian_pack_cheese-trash" \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/backpack_vr.dm b/code/game/objects/items/weapons/storage/backpack_vr.dm index 5588969224..1a872e99ac 100644 --- a/code/game/objects/items/weapons/storage/backpack_vr.dm +++ b/code/game/objects/items/weapons/storage/backpack_vr.dm @@ -45,8 +45,9 @@ return var/datum/sprite_accessory/tail/taur/TT = H.tail_style - item_state = "[icon_base]_[TT.icon_sprite_tag]" //icon_sprite_tag is something like "deer" - return 1 + if(istype(TT)) + item_state = "[icon_base]_[TT.icon_sprite_tag]" //icon_sprite_tag is something like "deer" + return 1 diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index 40ab9cb45e..db7644476b 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -100,7 +100,7 @@ /obj/item/weapon/melee/baton/examine(mob/user) . = ..() - if(Adjacent(user, src)) + if(Adjacent(user)) if(bcell) . += "The baton is [round(bcell.percent())]% charged." if(!bcell) diff --git a/code/game/objects/micro_structures.dm b/code/game/objects/micro_structures.dm index 15bf9d13ae..8b10f31fa5 100644 --- a/code/game/objects/micro_structures.dm +++ b/code/game/objects/micro_structures.dm @@ -10,6 +10,13 @@ var/magic = FALSE //For events and stuff, if true, this tunnel will show up in the list regardless of whether it's in valid range, of if you're in a tunnel with this var, all tunnels of the same faction will show up redardless of range micro_target = TRUE + var/static/non_micro_types = list( + /mob/living/simple_mob/vore/alienanimals/catslug, + /mob/living/simple_mob/vore/hostile/morph, + /mob/living/simple_mob/protean_blob, + /mob/living/simple_mob/slime + ) + /obj/structure/micro_tunnel/Initialize() . = ..() if(name == initial(name)) @@ -47,11 +54,19 @@ if(8) pixel_x = -32 -/obj/structure/micro_tunnel/attack_hand(mob/user) +/obj/structure/micro_tunnel/attack_hand(mob/living/user) if(!isliving(user)) return ..() if(user.loc == src) - var/choice = tgui_alert(user,"It's dark and gloomy in here. What would you like to do?","Tunnel",list("Exit", "Move", "Cancel")) + var/list/our_options = list("Exit", "Move") + + if(is_type_in_list(user, non_micro_types)) + if(src.contents.len > 1) + our_options |= "Eat" + + our_options |= "Cancel" + + var/choice = tgui_alert(user,"It's dark and gloomy in here. What would you like to do?","Tunnel",our_options) switch(choice) if("Exit") if(user.loc != src) @@ -114,6 +129,28 @@ var/obj/structure/micro_tunnel/da_oddawun = choice da_oddawun.tunnel_notify(user) return + if("Eat") + var/list/our_targets = list() + for(var/mob/living/L in src.contents) + if(L == user) + continue + our_targets |= L + if(!our_targets.len) + to_chat(user, "There is no one in here except for you!") + return + var/mob/our_choice + if(our_targets.len == 1) + our_choice = pick(our_targets) + else + our_choice = tgui_input_list(user, "Who would you like to eat?", "Pick a target to eat", our_targets) + if(user.loc != src) + to_chat(user, "You are no longer inside \the [src], and so cannot eat \the [our_choice].") + return + if(our_choice.loc != src) + to_chat(user, "\The [our_choice] is no longer inside \the [src], and so cannot be eaten.") + return + user.feed_grabbed_to_self(user,our_choice) + return if("Cancel") return @@ -157,8 +194,6 @@ user.visible_message("\The [user] pulls \the [grabbed] out of \the [src]! ! !") return - if(tgui_alert(user,"Do you want to go into the tunnel?","Enter Tunnel",list("Yes", "No")) != "Yes") - return user.visible_message("\The [user] begins climbing into \the [src]!") if(!do_after(user, 10 SECONDS, exclusive = TRUE)) to_chat(user, "You didn't go into \the [src]!") @@ -170,6 +205,10 @@ if(user.mob_size <= MOB_TINY || user.get_effective_size(TRUE) <= micro_accepted_scale) return TRUE + if(is_type_in_list(user, non_micro_types)) + if(tgui_alert(user, "Would you like to enter the tunnel, or reach inside it?", "Enter or reach", list("Enter","Reach")) == "Enter") + return TRUE + return FALSE /obj/structure/micro_tunnel/attack_generic(mob/user, damage, attack_verb) diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm index afa263a8ac..51c32a1f00 100644 --- a/code/game/objects/structures/artstuff.dm +++ b/code/game/objects/structures/artstuff.dm @@ -106,7 +106,7 @@ if(choice == "No") return var/basecolor = input(user, "Select a base color for the canvas:", "Base Color", canvas_color) as null|color - if(basecolor && Adjacent(user, src) && Adjacent(user, I)) + if(basecolor && Adjacent(user) && user.get_active_hand() == I) canvas_color = basecolor reset_grid() user.visible_message("[user] smears paint on [src], covering the entire thing in paint.", "You smear paint on [src], changing the color of the entire thing.", runemessage = "smears paint") diff --git a/code/game/objects/structures/crates_lockers/largecrate_vr.dm b/code/game/objects/structures/crates_lockers/largecrate_vr.dm index b70833ccea..3b35be2479 100644 --- a/code/game/objects/structures/crates_lockers/largecrate_vr.dm +++ b/code/game/objects/structures/crates_lockers/largecrate_vr.dm @@ -56,7 +56,7 @@ /mob/living/simple_mob/otie/zorgoia, //CHOMPstation edit /mob/living/simple_mob/vore/rabbit, /mob/living/simple_mob/vore/weretiger;0.5, -// /mob/living/simple_mob/otie;0.5 + /mob/living/simple_mob/otie;0.5 )) return ..() @@ -72,11 +72,12 @@ /mob/living/simple_mob/animal/space/alien/drone, /mob/living/simple_mob/animal/space/alien/sentinel, /mob/living/simple_mob/animal/space/alien/queen, -// /mob/living/simple_mob/otie/feral, -// /mob/living/simple_mob/otie/red, + /mob/living/simple_mob/otie/feral, + /mob/living/simple_mob/otie/feral/chubby, + /mob/living/simple_mob/otie/red, /mob/living/simple_mob/vore/aggressive/corrupthound)) return ..() -/* + /obj/structure/largecrate/animal/guardbeast name = "VARMAcorp autoNOMous security solution" desc = "The VARMAcorp bioengineering division flagship product on trained optimal snowflake guard dogs." @@ -84,6 +85,11 @@ icon_state = "sotiecrate" starts_with = list(/mob/living/simple_mob/otie/security) +/obj/structure/largecrate/animal/otie/guardbeast/Initialize() + starts_with = list(pick(/mob/living/simple_mob/otie/security, + /mob/living/simple_mob/otie/security/chubby)) + return ..() + /obj/structure/largecrate/animal/guardmutant name = "VARMAcorp autoNOMous security solution for hostile environments." desc = "The VARMAcorp bioengineering division flagship product on trained optimal snowflake guard dogs. This one can survive hostile atmosphere." @@ -91,6 +97,12 @@ icon_state = "sotiecrate" starts_with = list(/mob/living/simple_mob/otie/security/phoron) +/obj/structure/largecrate/animal/otie/guardmutant/Initialize() + starts_with = list(pick(/mob/living/simple_mob/otie/security/phoron;2, + /mob/living/simple_mob/otie/security/phoron/red;0.5, + /mob/living/simple_mob/otie/security/phoron/red/chubby;0.5)) + return ..() + /obj/structure/largecrate/animal/otie name = "VARMAcorp adoptable reject (Dangerous!)" desc = "A warning on the side says the creature inside was returned to the supplier after injuring or devouring several unlucky members of the previous adoption family. It was given a second chance with the next customer. Godspeed and good luck with your new pet!" @@ -99,6 +111,11 @@ starts_with = list(/mob/living/simple_mob/otie/cotie) var/taped = 1 +/obj/structure/largecrate/animal/otie/Initialize() + starts_with = list(pick(/mob/living/simple_mob/otie/cotie, + /mob/living/simple_mob/otie/cotie/chubby)) + return ..() + /obj/structure/largecrate/animal/otie/phoron name = "VARMAcorp adaptive beta subject (Experimental)" desc = "VARMAcorp experimental hostile environment adaptive breeding development kit. WARNING, DO NOT RELEASE IN WILD!" @@ -106,7 +123,8 @@ /obj/structure/largecrate/animal/otie/phoron/Initialize() starts_with = list(pick(/mob/living/simple_mob/otie/cotie/phoron;2, - /mob/living/simple_mob/otie/red/friendly;0.5)) + /mob/living/simple_mob/otie/red/friendly;0.5, + /mob/living/simple_mob/otie/red/chubby;0.5)) return ..() /obj/structure/largecrate/animal/otie/attack_hand(mob/living/carbon/human/M as mob)//I just couldn't decide between the icons lmao @@ -115,7 +133,6 @@ icon_state = "otiecrate" taped = 0 ..() -*/ //VORESTATION AI REMOVAL, Oties are still fucking broken. /obj/structure/largecrate/animal/catgirl name = "Catgirl Crate" diff --git a/code/modules/admin/admin_verb_lists_vr.dm b/code/modules/admin/admin_verb_lists_vr.dm index cbbeab71ad..c7f6745c43 100644 --- a/code/modules/admin/admin_verb_lists_vr.dm +++ b/code/modules/admin/admin_verb_lists_vr.dm @@ -8,7 +8,7 @@ var/list/admin_verbs_default = list( /client/proc/cmd_mod_say, //VOREStation Add, /client/proc/cmd_event_say, //VOREStation Add, /client/proc/cmd_mentor_ticket_panel, - /client/proc/cmd_mentor_say + /client/proc/cmd_mentor_say, // /client/proc/hide_verbs, //hides all our adminverbs, //VOREStation Remove, // /client/proc/hide_most_verbs, //hides all our hideable adminverbs, //VOREStation Remove, // /client/proc/debug_variables, //allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify, //VOREStation Remove, @@ -18,6 +18,8 @@ var/list/admin_verbs_default = list( // /client/proc/cmd_mod_say, // /client/proc/deadchat //toggles deadchat on/off, // /client/proc/toggle_ahelp_sound, + /client/proc/toggle_admin_global_looc, + /client/proc/toggle_admin_deadchat ) var/list/admin_verbs_admin = list( diff --git a/code/modules/admin/verbs/buildmode.dm b/code/modules/admin/verbs/buildmode.dm index d9aacdcaca..485cc88e22 100644 --- a/code/modules/admin/verbs/buildmode.dm +++ b/code/modules/admin/verbs/buildmode.dm @@ -364,7 +364,7 @@ if(ispath(holder.buildmode.objholder,/turf)) var/turf/T = get_turf(object) T.ChangeTurf(holder.buildmode.objholder) - else + else if(ispath(holder.buildmode.objholder)) var/obj/A = new holder.buildmode.objholder (get_turf(object)) A.set_dir(holder.builddir.dir) else if(pa.Find("right")) diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index 2427b2441d..9fa878ad72 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -16,7 +16,7 @@ to_chat(usr, " You cannot pray (muted).") return - var/image/cross = image('icons/obj/storage.dmi',"bible") + var/icon/cross = icon('icons/obj/storage.dmi',"bible") msg = "\icon[cross][bicon(cross)] PRAY: [key_name(src, 1)] (?) (PP) (VV) (SM) ([admin_jump_link(src, src)]) (CA) (SC) (SMITE): [msg]" for(var/client/C in GLOB.admins) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 960d3a6b99..ceba8367ff 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -530,8 +530,9 @@ Traitors and the like can also be revived with the previous role mostly intact. if(equipment) if(charjob) job_master.EquipRank(new_character, charjob, 1, announce) - new_character.mind.assigned_role = charjob - new_character.mind.role_alt_title = job_master.GetPlayerAltTitle(new_character, charjob) + if(new_character.mind) + new_character.mind.assigned_role = charjob + new_character.mind.role_alt_title = job_master.GetPlayerAltTitle(new_character, charjob) equip_custom_items(new_character) //CHOMPEdit readded to enable custom_item.txt //If desired, add records. diff --git a/code/modules/admin/verbs/smite_vr.dm b/code/modules/admin/verbs/smite_vr.dm index 750a8c0bea..5ac60fcfc1 100644 --- a/code/modules/admin/verbs/smite_vr.dm +++ b/code/modules/admin/verbs/smite_vr.dm @@ -146,6 +146,7 @@ var/redspace_abduction_z redspace_abduction_z = -1 to_chat(user,"This is the first use of the verb this shift, it will take a minute to configure the abduction z-level. It will be z[world.maxz+1].") var/z = ++world.maxz + world.max_z_changed() for(var/x = 1 to world.maxx) for(var/y = 1 to world.maxy) var/turf/T = locate(x,y,z) @@ -232,7 +233,7 @@ var/redspace_abduction_z to_chat(target, "Autosaving your progress, please wait...") target << 'sound/effects/ding.ogg' - + var/static/list/bad_tips = list( "Did you know that black shoes protect you from electrocution while hacking?", "Did you know that airlocks always have a wire that disables ID checks?", diff --git a/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm b/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm index 6986f5e23c..210a41239f 100644 --- a/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm +++ b/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm @@ -123,6 +123,12 @@ /datum/ai_holder/simple_mob/xenobio_slime/handle_special_tactic() evolve_and_reproduce() +/datum/ai_holder/simple_mob/xenobio_slime/handle_stance_tactical() + if(!istype(holder) || QDELETED(holder)) + qdel(src) + return + ..() + // Hit the correct verbs to keep the slime species going. /datum/ai_holder/simple_mob/xenobio_slime/proc/evolve_and_reproduce() var/mob/living/simple_mob/slime/xenobio/my_slime = holder diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index cff2875e7d..98e1c2d51d 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -208,6 +208,8 @@ prefs.last_id = computer_id //these are gonna be used for banning prefs.client = src // Only relevant if we reloaded it from the global list, otherwise prefs/New sets it + hook_vr("client_new",list(src)) //VOREStation Code. For now this only loads vore prefs, so better put before mob.Login() call but after normal prefs are loaded. + . = ..() //calls mob.Login() prefs.sanitize_preferences() if(prefs) @@ -252,8 +254,6 @@ if(config.aggressive_changelog) src.changes() - hook_vr("client_new",list(src)) //VOREStation Code - if(config.paranoia_logging) var/alert = FALSE //VOREStation Edit start. if(isnum(player_age) && player_age == 0) diff --git a/code/modules/client/preference_setup/general/02_language.dm b/code/modules/client/preference_setup/general/02_language.dm index 25d8c2c10e..9ceb693bd2 100644 --- a/code/modules/client/preference_setup/general/02_language.dm +++ b/code/modules/client/preference_setup/general/02_language.dm @@ -9,7 +9,8 @@ /datum/category_item/player_setup_item/general/language/load_character(var/savefile/S) S["language"] >> pref.alternate_languages S["extra_languages"] >> pref.extra_languages - testing("LANGSANI: Loaded from [pref.client]'s character [pref.real_name || "-name not yet loaded-"] savefile: [english_list(pref.alternate_languages || list())]") + if(islist(pref.alternate_languages)) // Because aparently it may not be? + testing("LANGSANI: Loaded from [pref.client]'s character [pref.real_name || "-name not yet loaded-"] savefile: [english_list(pref.alternate_languages || list())]") S["language_prefixes"] >> pref.language_prefixes //CHOMPEdit Begin S["species"] >> pref.species @@ -19,7 +20,8 @@ /datum/category_item/player_setup_item/general/language/save_character(var/savefile/S) S["language"] << pref.alternate_languages S["extra_languages"] << pref.extra_languages - testing("LANGSANI: Saved to [pref.client]'s character [pref.real_name || "-name not yet loaded-"] savefile: [english_list(pref.alternate_languages || list())]") + if(islist(pref.alternate_languages)) // Because aparently it may not be? + testing("LANGSANI: Loaded from [pref.client]'s character [pref.real_name || "-name not yet loaded-"] savefile: [english_list(pref.alternate_languages || list())]") S["language_prefixes"] << pref.language_prefixes S["language_custom_keys"] << pref.language_custom_keys diff --git a/code/modules/client/preference_setup/global/setting_datums.dm b/code/modules/client/preference_setup/global/setting_datums.dm index 11ef5d994b..f7ca6bf963 100644 --- a/code/modules/client/preference_setup/global/setting_datums.dm +++ b/code/modules/client/preference_setup/global/setting_datums.dm @@ -422,3 +422,9 @@ var/list/_client_preferences_by_type key = "CHAT_RLOOC" enabled_description = "Show" disabled_description = "Hide" + +/datum/client_preference/holder/show_staff_dsay + description ="Staff Deadchat" + key = "CHAT_ADSAY" + enabled_description = "Show" + disabled_description = "Hide" diff --git a/code/modules/client/preference_setup/loadout/loadout_head_vr.dm b/code/modules/client/preference_setup/loadout/loadout_head_vr.dm index c41474a68e..3ddc8c5388 100644 --- a/code/modules/client/preference_setup/loadout/loadout_head_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_head_vr.dm @@ -49,6 +49,14 @@ display_name = "pink tiger pelt" path = /obj/item/clothing/head/pelt/tigerpeltpink +/datum/gear/head/magic_hat + display_name = "wizard hat, colorable" + path = /obj/item/clothing/head/wizard/fake/realistic/colorable + +/datum/gear/head/magic_hat/New() + ..() + gear_tweaks += gear_tweak_free_color_choice + /* Talon hats */ diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm index 3041af9e90..14cc60208d 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm @@ -194,6 +194,11 @@ path = /obj/item/clothing/suit/storage/apron/overalls cost = 1 +/datum/gear/suit/altevian_apron + display_name = "crafters pride apron" + path = /obj/item/clothing/suit/storage/apron/altevian + cost = 1 + /datum/gear/suit/cyberpunk display_name = "cyberpunk jacket" path = /obj/item/clothing/suit/cyberpunk diff --git a/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm b/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm index 6272e40204..9e4a87cb6d 100644 --- a/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm @@ -94,5 +94,14 @@ /datum/gear/suit/taur/white_dress display_name = "white wedding dress (Wolf/Horse-taur)" - path = /obj/item/clothing/suit/taur_dress/white + path = /obj/item/clothing/suit/taur/dress sort_category = "Xenowear" + +/datum/gear/suit/taur/colorable_skirt + display_name = "colorable skirt (Wolf/Horse-taur)" + path = /obj/item/clothing/suit/taur/skirt + sort_category = "Xenowear" + +/datum/gear/suit/taur/colorable_skirt/New() + ..() + gear_tweaks += gear_tweak_free_color_choice \ No newline at end of file diff --git a/code/modules/client/preferences_toggle_procs.dm b/code/modules/client/preferences_toggle_procs.dm index 6df855d1eb..0e83638e88 100644 --- a/code/modules/client/preferences_toggle_procs.dm +++ b/code/modules/client/preferences_toggle_procs.dm @@ -512,7 +512,7 @@ CHOMPRemove. Bundled voice sounds into emote/whisper/subtle. Going this extra le to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] receive debug logs.") SScharacter_setup.queue_preferences_save(prefs) - feedback_add_details("admin_verb","TBeSpecial") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + feedback_add_details("admin_verb","TADebugLogs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! //Mods /client/proc/toggle_attack_logs() @@ -527,4 +527,33 @@ CHOMPRemove. Bundled voice sounds into emote/whisper/subtle. Going this extra le to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] receive attack logs.") SScharacter_setup.queue_preferences_save(prefs) - feedback_add_details("admin_verb","TBeSpecial") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + feedback_add_details("admin_verb","TAAttackLogs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +//General +/client/proc/toggle_admin_global_looc() + set name = "Toggle Admin Global LOOC Visibility" + set category = "Preferences" + set desc = "Toggles seeing LOOC messages outside your actual LOOC range." + + var/pref_path = /datum/client_preference/holder/show_rlooc + + if(holder) + toggle_preference(pref_path) + to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear global LOOC.") + SScharacter_setup.queue_preferences_save(prefs) + + feedback_add_details("admin_verb","TAGlobalLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_admin_deadchat() + set name = "Toggle Admin Living Deadchat" + set category = "Preferences" + set desc = "Toggles seeing deadchat while not observing." + + var/pref_path = /datum/client_preference/holder/show_staff_dsay + + if(holder) + toggle_preference(pref_path) + to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear deadchat while not observing.") + SScharacter_setup.queue_preferences_save(prefs) + + feedback_add_details("admin_verb","TADeadchat") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/client/preferences_vr.dm b/code/modules/client/preferences_vr.dm index 8b0b72f8bb..df898e15f6 100644 --- a/code/modules/client/preferences_vr.dm +++ b/code/modules/client/preferences_vr.dm @@ -100,7 +100,7 @@ else to_chat(src, "You are now catchable.") prefs.capture_crystal = 1 - if(L) + if(L && istype(L)) L.capture_crystal = prefs.capture_crystal SScharacter_setup.queue_preferences_save(prefs) diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index d2ba4b26d9..1341af1b69 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -19,6 +19,12 @@ icon_state = "apron_white" item_state_slots = list(slot_r_hand_str = "apronchef", slot_l_hand_str = "apronchef") +/obj/item/clothing/suit/storage/apron/altevian + name = "Multi-purpose Crafters' Pride" + desc = "An apron designed by the space faring species that can hold an array of tools and other trinkets. It's made with a high-quality material." + icon_state = "apron_altevian" + item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) + //Captain /obj/item/clothing/suit/captunic name = "site manager's parade tunic" diff --git a/code/modules/clothing/suits/miscellaneous_vr.dm b/code/modules/clothing/suits/miscellaneous_vr.dm index 94fb746d94..9a10952789 100644 --- a/code/modules/clothing/suits/miscellaneous_vr.dm +++ b/code/modules/clothing/suits/miscellaneous_vr.dm @@ -74,17 +74,23 @@ desc = "Knightly armor for a mount who doesn't need any rider. This one is marked to the house of Mason." icon_state = "Mason_barding" -/obj/item/clothing/suit/taur_dress +/obj/item/clothing/suit/taur icon = 'icons/mob/taursuits_horse_vr.dmi' body_parts_covered = UPPER_TORSO|LOWER_TORSO pixel_x = -16 -/obj/item/clothing/suit/taur_dress/white +/obj/item/clothing/suit/taur/dress name = "white wedding dress" desc = "A fancy white dress with a blue underdress." icon_state = "whitedress1" flags_inv = HIDESHOES +/obj/item/clothing/suit/taur/skirt + name = "taur skirt" + desc = "A skirt with a corset, fit for those with four legs." + icon_state = "skirt_colorable" + flags_inv = HIDESHOES + /obj/item/clothing/suit/storage/det_trench/alt name = "sleek modern coat" desc = "A sleek overcoat made of neo-laminated fabric. Has a reasonably sized pocket on the inside." diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm index 30cb1d6b55..054f9dd110 100644 --- a/code/modules/clothing/suits/wiz_robe.dm +++ b/code/modules/clothing/suits/wiz_robe.dm @@ -18,6 +18,16 @@ desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." icon_state = "wizard-fake" body_parts_covered = HEAD|FACE + siemens_coefficient = 1 + +/obj/item/clothing/head/wizard/fake/realistic + desc = "A cool-looking 'magic' hat." + icon_state = "wizard" + body_parts_covered = HEAD + +/obj/item/clothing/head/wizard/fake/realistic/colorable + desc = "A cool-looking 'magic' hat." + icon_state = "wizard-white" /obj/item/clothing/head/wizard/marisa name = "Witch Hat" diff --git a/code/modules/economy/vending_machines_vr.dm b/code/modules/economy/vending_machines_vr.dm index dbe287f9df..668208216b 100644 --- a/code/modules/economy/vending_machines_vr.dm +++ b/code/modules/economy/vending_machines_vr.dm @@ -3548,8 +3548,14 @@ product_ads = "Perfectly edible!;Squeaky clean foods!;Cheesed to meet you!;Made for spacers, by spacers, of spacers!" products = list(/obj/item/weapon/reagent_containers/food/snacks/ratprotein = 15, /obj/item/weapon/reagent_containers/food/snacks/ratveggies = 15, - /obj/item/weapon/reagent_containers/food/snacks/ratliquid = 15) + /obj/item/weapon/reagent_containers/food/snacks/ratliquid = 15, + /obj/item/weapon/reagent_containers/food/snacks/ratfruitcake = 15, + /obj/item/weapon/reagent_containers/food/snacks/ratpackburger = 8, + /obj/item/weapon/reagent_containers/food/snacks/ratpackcheese = 8) prices = list(/obj/item/weapon/reagent_containers/food/snacks/ratprotein = 8, /obj/item/weapon/reagent_containers/food/snacks/ratveggies = 8, - /obj/item/weapon/reagent_containers/food/snacks/ratliquid = 8) + /obj/item/weapon/reagent_containers/food/snacks/ratliquid = 8, + /obj/item/weapon/reagent_containers/food/snacks/ratfruitcake = 8, + /obj/item/weapon/reagent_containers/food/snacks/ratpackburger = 10, + /obj/item/weapon/reagent_containers/food/snacks/ratpackcheese = 10) diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm index f2675a0ede..d5eb151d3f 100644 --- a/code/modules/events/prison_break.dm +++ b/code/modules/events/prison_break.dm @@ -64,7 +64,7 @@ var/obj/machinery/power/apc/theAPC = null for(var/area/A in areas) theAPC = A.get_apc() - if(theAPC.operating) //If the apc's off, it's a little hard to overload the lights. + if(theAPC && theAPC.operating) //If the apc's off, it's a little hard to overload the lights. for(var/obj/machinery/light/L in A) L.flicker(10) diff --git a/code/modules/events/spontaneous_appendicitis.dm b/code/modules/events/spontaneous_appendicitis.dm index ddc392174e..241ed83db0 100644 --- a/code/modules/events/spontaneous_appendicitis.dm +++ b/code/modules/events/spontaneous_appendicitis.dm @@ -1,4 +1,7 @@ /datum/event/spontaneous_appendicitis/start() + if(prob(50)) + kill() + return for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) if(H.client && H.appendicitis()) break diff --git a/code/modules/events/viral_infection.dm b/code/modules/events/viral_infection.dm index 9be0589077..5ee99ed5db 100644 --- a/code/modules/events/viral_infection.dm +++ b/code/modules/events/viral_infection.dm @@ -4,6 +4,9 @@ var/global/list/event_viruses = list() // so that event viruses are kept around var/list/viruses = list() /datum/event/viral_infection/setup() + if(prob(50)) + kill() + return announceWhen = rand(0, 3000) endWhen = announceWhen + 1 diff --git a/code/modules/food/food/drinks/bottle.dm b/code/modules/food/food/drinks/bottle.dm index df16e4960b..9dcd17c256 100644 --- a/code/modules/food/food/drinks/bottle.dm +++ b/code/modules/food/food/drinks/bottle.dm @@ -12,6 +12,7 @@ var/obj/item/weapon/reagent_containers/glass/rag/rag = null var/rag_underlay = "rag" + var/violent_throw = FALSE /obj/item/weapon/reagent_containers/food/drinks/bottle/on_reagent_change() return // To suppress price updating. Bottles have their own price tags. @@ -29,18 +30,26 @@ return ..() //when thrown on impact, bottles smash and spill their contents +/obj/item/weapon/reagent_containers/food/drinks/bottle/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, datum/callback/callback) + . = ..() + if(istype(thrower) && thrower.a_intent == I_HURT) + violent_throw = TRUE + throw_source = get_turf(thrower) + /obj/item/weapon/reagent_containers/food/drinks/bottle/throw_impact(atom/hit_atom, var/speed) ..() - var/mob/M = thrower - if(isGlass && istype(M) && M.a_intent == I_HURT) + if(isGlass && violent_throw) var/throw_dist = get_dist(throw_source, loc) - if(speed >= throw_speed && smash_check(throw_dist)) //not as reliable as smashing directly + if(smash_check(throw_dist)) //not as reliable as smashing directly if(reagents) hit_atom.visible_message("The contents of \the [src] splash all over [hit_atom]!") reagents.splash(hit_atom, reagents.total_volume) src.smash(loc, hit_atom) + violent_throw = FALSE + throw_source = null + /obj/item/weapon/reagent_containers/food/drinks/bottle/proc/smash_check(var/distance) if(!isGlass || !smash_duration) return 0 diff --git a/code/modules/food/food/snacks.dm b/code/modules/food/food/snacks.dm index 11d7a172dc..ad34247ece 100644 --- a/code/modules/food/food/snacks.dm +++ b/code/modules/food/food/snacks.dm @@ -34,6 +34,8 @@ var/package_trash /// Packaged meals switch to this state when opened, if set var/package_open_state + /// Packaged meals that have opening animation + var/package_opening_state /// If this is canned. If true, it will print a message and ask you to open it var/canned = FALSE @@ -269,6 +271,8 @@ user.put_in_hands(T) if(package_open_state) icon_state = package_open_state + if(package_opening_state) + flick(package_opening_state, src) /obj/item/weapon/reagent_containers/food/snacks/proc/uncan(mob/user) canned = FALSE @@ -6897,7 +6901,7 @@ nutriment_desc = list("apple" = 1, "sweetness" = 1) bitesize = 2 -/obj/item/weapon/reagent_containers/food/snacks/appleberry/Initialize() +/obj/item/weapon/reagent_containers/food/snacks/canned/appleberry/Initialize() . = ..() reagents.add_reagent("milk", 8) reagents.add_reagent("sugar", 5) diff --git a/code/modules/food/food/snacks_vr.dm b/code/modules/food/food/snacks_vr.dm index 23935f6207..3ba39dc3ad 100644 --- a/code/modules/food/food/snacks_vr.dm +++ b/code/modules/food/food/snacks_vr.dm @@ -826,3 +826,38 @@ /obj/item/weapon/reagent_containers/food/snacks/ratsteak/Initialize() . = ..() reagents.add_reagent("protein", 3) + +/obj/item/weapon/reagent_containers/food/snacks/ratfruitcake + name = "Premade Fruit Block" + desc = "A block of processed material that is infused with a mix of fruits and matter of such." + icon = 'icons/obj/food_vr.dmi' + icon_state = "altevian_fruitcake" + package_open_state = "altevian_fruitcake-open" + package = TRUE + trash = /obj/item/trash/ratfruitcake + nutriment_amt = 2 + nutriment_desc = list("fruitiness" = 4) + +/obj/item/weapon/reagent_containers/food/snacks/ratpackburger + name = "Altevian Prepackaged Meal - Burger" + desc = "A unique twist on what most know as MREs. This seems to be made with using bluespace tech and other methods of preserving an items freshness that it's like someone just ordered this from a restaurant just minutes ago. This one seems to be of burger and fries!" + icon = 'icons/obj/food_vr.dmi' + icon_state = "altevian_pack_burger" + package_open_state = "altevian_pack_burger-open" + package_opening_state = "altevian_pack_burger-opening" + package = TRUE + trash = /obj/item/trash/ratpackburger + nutriment_amt = 2 + nutriment_desc = list("fresh buns" = 2, "burger patty" = 4, "pickles" = 1) + +/obj/item/weapon/reagent_containers/food/snacks/ratpackcheese + name = "Generations Novelty Packaged Wedge" + desc = "Using the popular method of packaging that altevians use, they seemed to also use it for other methods. This one appears to have no real markings on it, save for its different coloring, and an image of the altevian emblem." + icon = 'icons/obj/food_vr.dmi' + icon_state = "altevian_pack_cheese" + package_open_state = "altevian_pack_cheese-open" + package_opening_state = "altevian_pack_cheese-opening" + package = TRUE + trash = /obj/item/trash/ratpackcheese + nutriment_amt = 2 + nutriment_desc = list("gourmand cheese" = 4) diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm index 52ad80641a..c3f1a6dc3e 100644 --- a/code/modules/games/cards.dm +++ b/code/modules/games/cards.dm @@ -400,11 +400,13 @@ /obj/item/weapon/hand/update_icon(var/direction = 0) - if(!cards.len) + var/cardNumber = cards.len + + if(!cardNumber) qdel(src) return - else if(cards.len > 1) - name = "hand of cards" + else if(cardNumber > 1) + name = "hand of cards ([cardNumber])" desc = "Some playing cards." else name = "a playing card" @@ -413,7 +415,7 @@ cut_overlays() - if(cards.len == 1) + if(cardNumber == 1) var/datum/playingcard/P = cards[1] var/image/I = new(src.icon, (concealed ? "[P.back_icon]" : "[P.card_icon]") ) I.pixel_x += (-5+rand(10)) @@ -421,7 +423,7 @@ add_overlay(I) return - var/offset = FLOOR(20/cards.len, 1) + var/offset = FLOOR(20/cardNumber, 1) var/matrix/M = matrix() if(direction) @@ -453,6 +455,7 @@ add_overlay(I) i++ + /obj/item/weapon/hand/dropped(mob/user as mob) if(locate(/obj/structure/table, loc)) src.update_icon(user.dir) diff --git a/code/modules/mob/language/station_vr.dm b/code/modules/mob/language/station_vr.dm index 7be2976d69..77186ccb00 100644 --- a/code/modules/mob/language/station_vr.dm +++ b/code/modules/mob/language/station_vr.dm @@ -15,7 +15,7 @@ speech_verb = "chirps" colour = "birdsongc" key = "G" - syllables = list ("cheep", "peep", "tweet") + syllables = list ("chee", "pee", "twee", "hoo", "ee", "oo", "ch", "ts", "sch", "twe", "too", "pha", "ewe", "shee", "shoo", "p", "tw", "aw", "caw", "c") /datum/language/sergal name = LANGUAGE_SAGARU diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index bec833476f..1d36c892cd 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -297,7 +297,8 @@ return /mob/living/bot/proc/handleFrustrated(var/targ) - obstacle = targ ? target_path[1] : patrol_path[1] + if((targ && LAZYLEN(target_path)) || LAZYLEN(patrol_path)) + obstacle = targ ? target_path[1] : patrol_path[1] target_path = list() patrol_path = list() return @@ -577,4 +578,4 @@ feeding = FALSE can_be_drop_pred = FALSE - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/carbon/human/life_vr.dm b/code/modules/mob/living/carbon/human/life_vr.dm index fc4f613418..8e52e3daaf 100644 --- a/code/modules/mob/living/carbon/human/life_vr.dm +++ b/code/modules/mob/living/carbon/human/life_vr.dm @@ -78,3 +78,8 @@ // Moving around increases germ_level faster if(germ_level < GERM_LEVEL_MOVE_CAP && prob(8)) germ_level++ + + +/mob/living/carbon + var/synth_cosmetic_pain = FALSE + diff --git a/code/modules/mob/living/carbon/human/npcs.dm b/code/modules/mob/living/carbon/human/npcs.dm index 23f7bd722a..3aa2c1bd92 100644 --- a/code/modules/mob/living/carbon/human/npcs.dm +++ b/code/modules/mob/living/carbon/human/npcs.dm @@ -12,3 +12,4 @@ real_name = name w_uniform = new /obj/item/clothing/under/punpun(src) regenerate_icons() + can_be_drop_prey = TRUE //CHOMP Add diff --git a/code/modules/mob/living/carbon/human/species/outsider/vox.dm b/code/modules/mob/living/carbon/human/species/outsider/vox.dm index da0e0e0b9b..5093c4e821 100644 --- a/code/modules/mob/living/carbon/human/species/outsider/vox.dm +++ b/code/modules/mob/living/carbon/human/species/outsider/vox.dm @@ -46,6 +46,7 @@ breath_type = "nitrogen" //CHOMPedit poison_type = "oxygen" + ideal_air_type = /datum/gas_mixture/belly_air/vox siemens_coefficient = 0.2 flags = NO_SCAN | NO_DEFIB @@ -110,6 +111,3 @@ H.internal = locate(/obj/item/weapon/tank) in H.contents if(istype(H.internal,/obj/item/weapon/tank) && H.internals) H.internals.icon_state = "internal1" - -/datum/species/vox/get_perfect_belly_air_type() - return /datum/gas_mixture/belly_air/vox diff --git a/code/modules/mob/living/carbon/human/species/species_getters_vr.dm b/code/modules/mob/living/carbon/human/species/species_getters_vr.dm index 8bccade358..140923062c 100644 --- a/code/modules/mob/living/carbon/human/species/species_getters_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_getters_vr.dm @@ -7,4 +7,7 @@ return wing_animation /datum/species/proc/get_perfect_belly_air_type(var/mob/living/carbon/human/H) - return /datum/gas_mixture/belly_air //Default \ No newline at end of file + if(ideal_air_type) + return ideal_air_type //Whatever we want + else + return /datum/gas_mixture/belly_air //Default \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species/species_vr.dm b/code/modules/mob/living/carbon/human/species/species_vr.dm index ef4e8252bd..77d15d000a 100644 --- a/code/modules/mob/living/carbon/human/species/species_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_vr.dm @@ -30,6 +30,8 @@ var/list/copy_vars = list("base_species", "icobase", "deform", "tail", "tail_animation", "icobase_tail", "color_mult", "primitive_form", "appearance_flags", "flesh_color", "base_color", "blood_mask", "damage_mask", "damage_overlays", "move_trail", "has_floating_eyes") var/trait_points = 0 + var/ideal_air_type = null // Set to something else if you breathe something else from default composition. Used for inbelly air. + var/micro_size_mod = 0 // How different is our size for interactions that involve us being small? var/macro_size_mod = 0 // How different is our size for interactions that involve us being big? var/digestion_nutrition_modifier = 1 diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm index 5ff11b6c9c..d54928b455 100644 --- a/code/modules/mob/living/carbon/human/species/station/station.dm +++ b/code/modules/mob/living/carbon/human/species/station/station.dm @@ -426,6 +426,7 @@ hazard_low_pressure = 220 // Dangerously low pressure. safe_pressure = 400 poison_type = "nitrogen" // technically it's a partial pressure thing but IDK if we can emulate that + ideal_air_type = /datum/gas_mixture/belly_air/zaddat genders = list(FEMALE, PLURAL) //females are polyp-producing, infertile females and males are nigh-identical @@ -501,9 +502,6 @@ if(!(K in covered)) H.apply_damage(light_amount/4, BURN, K, 0, 0, "Abnormal growths") -/datum/species/zaddat/get_perfect_belly_air_type() - return /datum/gas_mixture/belly_air/zaddat - /datum/species/diona name = SPECIES_DIONA name_plural = "Dionaea" diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm index 1a7f06be8f..c4c55fa63e 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm @@ -1153,3 +1153,16 @@ "You are dragged below the water and feel yourself slipping directly into \the [src]'s [vore_selected]!") to_chat(src, "You successfully drag \the [target] into the water, slipping them into your [vore_selected].") target.forceMove(src.vore_selected) + + +/mob/living/carbon/human/proc/toggle_pain_module() + set name = "Toggle pain simulation." + set desc = "Turn on your pain simulation for that organic experience! Or turn it off for repairs, or if it's too much." + set category = "Abilities" + + if(synth_cosmetic_pain) + to_chat(src, " You turn off your pain simulators.") + else + to_chat(src, " You turn on your pain simulators ") + + synth_cosmetic_pain = !synth_cosmetic_pain diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm index 35a56f9b42..25bb2c5aaf 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm @@ -136,12 +136,12 @@ /datum/trait/negative/breathes/phoron name = "Phoron Breather" desc = "You breathe phoron instead of oxygen (which is poisonous to you), much like a Vox." - var_changes = list("breath_type" = "phoron", "poison_type" = "oxygen") + var_changes = list("breath_type" = "phoron", "poison_type" = "oxygen", "ideal_air_type" = /datum/gas_mixture/belly_air/vox) /datum/trait/negative/breathes/nitrogen name = "Nitrogen Breather" desc = "You breathe nitrogen instead of oxygen (which is poisonous to you). Incidentally, phoron isn't poisonous to breathe to you." - var_changes = list("breath_type" = "nitrogen", "poison_type" = "oxygen") + var_changes = list("breath_type" = "nitrogen", "poison_type" = "oxygen", "ideal_air_type" = /datum/gas_mixture/belly_air/nitrogen_breather) /datum/trait/negative/monolingual name = "Monolingual" diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm index 9732a96922..0f79dd7576 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm @@ -166,11 +166,12 @@ /datum/trait/neutral/synth_chemfurnace name = "Biofuel Processor" - desc = "You are able to gain energy through consuming and processing normal food. Energy-dense foods such as protein bars and survival food will yield the best results." + desc = "You are able to gain energy through consuming and processing normal food, at the cost of significantly slower recharging via cyborg chargers. Energy-dense foods such as protein bars and survival food will yield the best results." cost = 0 custom_only = FALSE can_take = SYNTHETICS var_changes = list("organic_food_coeff" = 0.75, "synthetic_food_coeff" = 1) //CHOMPEdit: Increase values + excludes = list(/datum/trait/neutral/biofuel_value_down) /datum/trait/neutral/glowing_eyes name = "Glowing Eyes" @@ -608,9 +609,21 @@ /datum/trait/neutral/biofuel_value_down name = "Discount Biofuel processor" - desc = "You are able to gain energy through consuming and processing normal food. Unfortunately, it is half as effective as premium models." + desc = "You are able to gain energy through consuming and processing normal food. Unfortunately, it is half as effective as premium models. On the plus side, you still recharge from charging stations fairly efficiently." cost = 0 custom_only = FALSE can_take = SYNTHETICS var_changes = list("organic_food_coeff" = 0, "synthetic_food_coeff" = 0.3, digestion_efficiency = 0.5) excludes = list(/datum/trait/neutral/synth_chemfurnace) + +/datum/trait/neutral/synth_cosmetic_pain + name = "Pain simulation" + desc = "You have added modules in your synthetic shell that simulates the sensation of pain. You are able to turn this on and off for repairs as needed or convenience at will." + cost = 0 + custom_only = FALSE + can_take = SYNTHETICS + + +/datum/trait/neutral/synth_cosmetic_pain/apply(var/datum/species/S,var/mob/living/carbon/human/H) + ..(S,H) + H.verbs |= /mob/living/carbon/human/proc/toggle_pain_module diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm index bea432f1ac..b61f3923e4 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm @@ -215,3 +215,11 @@ custom_only = FALSE varchange_type = TRAIT_VARCHANGE_MORE_BETTER */ + +/datum/trait/positive/trauma_tolerance //CHOMPEdit renamed because we already have pain_tolerance pathname for halloss damage resistance. + name = "Grit" + desc = "You can keep going a little longer, a little harder when you get hurt, Injuries only inflict 85% as much pain, and slowdown from pain is 85% as effective." + cost = 2 + var_changes = list("trauma_mod" = 0.85) + excludes = list(/datum/trait/negative/neural_hypersensitivity) + can_take = ORGANICS diff --git a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm index 55ba92b58c..ec054ab520 100644 --- a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm +++ b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm @@ -81,7 +81,7 @@ // Move the mind avatar.Sleeping(1) src.mind.transfer_to(avatar) - to_chat(avatar, "You have enterred Virtual Reality!\nAll normal gameplay rules still apply.\nWounds you suffer here won't persist when you leave VR, but some of the pain will.\nYou can leave VR at any time by using the \"Exit Virtual Reality\" verb in the Abilities tab, or by ghosting.\nYou can modify your appearance by using various \"Change \[X\]\" verbs in the Abilities tab.") + to_chat(avatar, "You have enterred Virtual Reality!\nAll normal gameplay rules still apply.\nWounds you suffer here won't persist when you leave VR, but some of the pain will.\nYou can leave VR at any time by using the \"Exit Virtual Reality\" verb in the Abilities tab, or by ghosting.") //No more prommie VR thing, so removed tidbit about changing appearance to_chat(avatar, " You black out for a moment, and wake to find yourself in a new body in virtual reality.") // So this is what VR feels like? // exit_vr is called on the vr mob, and puts the mind back into the original mob diff --git a/code/modules/mob/living/carbon/lick_wounds.dm b/code/modules/mob/living/carbon/lick_wounds.dm index 1bc94d0d98..8ec651d05b 100644 --- a/code/modules/mob/living/carbon/lick_wounds.dm +++ b/code/modules/mob/living/carbon/lick_wounds.dm @@ -1,12 +1,11 @@ -/mob/living/carbon/human/proc/lick_wounds(var/mob/living/carbon/M as mob in range(1)) // Allows the user to lick themselves. Given how rarely this trait is used, I don't see an issue with a slight buff. +/mob/living/carbon/human/proc/lick_wounds(var/mob/living/carbon/M as mob in view(1)) // Allows the user to lick themselves. Given how rarely this trait is used, I don't see an issue with a slight buff. set name = "Lick Wounds" set category = "Abilities" set desc = "Disinfect and heal small wounds with your saliva." - //CHOMPEdit Start - No longer usable while incapacitated - if(src.incapacitated()) + if(stat || paralysis || weakened || stunned) + to_chat(src, "You can't do that in your current state.") return - //CHOMPEdit End if(nutrition < 50) to_chat(src, "You need more energy to produce antiseptic enzymes. Eat something and try again.") diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm index 12914a0426..f6f37fa210 100644 --- a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm +++ b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm @@ -409,24 +409,33 @@ var/datum/matter_synth/glass = null /obj/item/device/lightreplacer/dogborg/attack_self(mob/user)//Recharger refill is so last season. Now we recycle without magic! - if(uses >= max_uses) - to_chat(user, "[src.name] is full.") + + var/choice = tgui_alert(user, "Do you wish to check the reserves or change the color?", "Selection List", list("Reserves", "Color")) + if(choice == "Color") + var/new_color = input(usr, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color) as color|null + if(new_color) + selected_color = new_color + to_chat(user, "The light color has been changed.") return - if(uses < max_uses && cooldown == 0) - if(glass.energy < 125) - to_chat(user, "Insufficient material reserves.") - return - to_chat(user, "It has [uses] lights remaining. Attempting to fabricate a replacement. Please stand still.") - cooldown = 1 - if(do_after(user, 50)) - glass.use_charge(125) - add_uses(1) - cooldown = 0 - else - cooldown = 0 else - to_chat(user, "It has [uses] lights remaining.") - return + if(uses >= max_uses) + to_chat(user, "[src.name] is full.") + return + if(uses < max_uses && cooldown == 0) + if(glass.energy < 125) + to_chat(user, "Insufficient material reserves.") + return + to_chat(user, "It has [uses] lights remaining. Attempting to fabricate a replacement. Please stand still.") + cooldown = 1 + if(do_after(user, 50)) + glass.use_charge(125) + add_uses(1) + cooldown = 0 + else + cooldown = 0 + else + to_chat(user, "It has [uses] lights remaining.") + return //Pounce stuff for K-9 /obj/item/weapon/dogborg/pounce diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm b/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm index 78593a85ac..128bffbc09 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm @@ -345,6 +345,19 @@ var/obj/item/device/dogborg/sleeper/B = new /obj/item/device/dogborg/sleeper(src) //So they can nom people and heal them B.water = water src.modules += B + + //CHOMPEdit Start - Give back the ATK/ABP since we don't have the surgeryhound + var/obj/item/stack/medical/advanced/ointment/O = new /obj/item/stack/medical/advanced/ointment(src) + var/obj/item/stack/medical/advanced/bruise_pack/P = new /obj/item/stack/medical/advanced/bruise_pack(src) + O.uses_charge = 1 + O.charge_costs = list(1000) + O.synths = list(medicine) + P.uses_charge = 1 + P.charge_costs = list(1000) + P.synths = list(medicine) + src.modules += O + src.modules += P + //CHOMPEdit End R.icon = 'icons/mob/widerobot_vr.dmi' diff --git a/code/modules/mob/living/simple_mob/simple_mob_vr.dm b/code/modules/mob/living/simple_mob/simple_mob_vr.dm index c11e9e183a..a8ac7e6cac 100644 --- a/code/modules/mob/living/simple_mob/simple_mob_vr.dm +++ b/code/modules/mob/living/simple_mob/simple_mob_vr.dm @@ -54,6 +54,7 @@ var/obj/item/device/radio/headset/mob_radio //Adminbus headset for simplemob shenanigans. does_spin = FALSE can_be_drop_pred = TRUE // Mobs are pred by default. + can_be_drop_prey = TRUE //CHOMP Add This also counts for spontaneous prey for telenoms and phase noms. var/damage_threshold = 0 //For some mobs, they have a damage threshold required to deal damage to them. diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/spookyghost.dm b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/spookyghost.dm index 13b88dd2e2..8bae5e7852 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/spookyghost.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/spookyghost.dm @@ -208,7 +208,8 @@ /mob/living/simple_mob/vore/alienanimals/spooky_ghost/apply_melee_effects(var/atom/A) var/mob/living/L = A - L.hallucination += rand(1,50) + if(L && istype(L)) + L.hallucination += rand(1,50) /mob/living/simple_mob/vore/alienanimals/spooky_ghost/Life() . = ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm index fb34c4d73f..6b37550c82 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm @@ -141,7 +141,8 @@ if(large_cocoon) C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") - ai_holder.remove_target() + if(ai_holder) + ai_holder.remove_target() return TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka.dm new file mode 100644 index 0000000000..8772a061d7 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka.dm @@ -0,0 +1,633 @@ +/datum/modifier/sifsap_salve + name = "Sifsap Salve" + desc = "Your wounds have been salved with Sivian sap." + mob_overlay_state = "cyan_sparkles" + stacks = MODIFIER_STACK_FORBID + on_created_text = "The glowing sap seethes and bubbles in your wounds, tingling and stinging." + on_expired_text = "The last of the sap in your wounds fizzles away." + +/datum/modifier/sifsap_salve/tick() + + if(holder.stat == DEAD || holder.isSynthetic()) + expire() + + if(istype(holder, /mob/living/simple_mob/animal/sif)) + + var/mob/living/simple_mob/animal/sif/critter = holder + if(critter.health >= (critter.getMaxHealth() * critter.sap_heal_threshold)) + return + + if(holder.resting) + if(istype(holder.loc, /obj/structure/animal_den)) + holder.adjustBruteLoss(-3) + holder.adjustFireLoss(-3) + holder.adjustToxLoss(-2) + else + holder.adjustBruteLoss(-2) + holder.adjustFireLoss(-2) + holder.adjustToxLoss(-1) + else + holder.adjustBruteLoss(-1) + holder.adjustFireLoss(-1) + +/obj/item/projectile/drake_spit + name = "drake spit" + icon_state = "ice_1" + damage = 0 + embed_chance = 0 + damage_type = BRUTE + muzzle_type = null + hud_state = "monkey" + combustion = FALSE + stun = 3 + weaken = 3 + eyeblur = 5 + fire_sound = 'sound/effects/splat.ogg' + +/obj/item/projectile/drake_spit/weak + stun = 0 + weaken = 0 + eyeblur = 2 + +/datum/category_item/catalogue/fauna/grafadreka + name = "Sivian Fauna - Grafadreka" + desc = {"Classification: S tesca pabulator +

+The reclusive grafadreka (Icelandic, lit. 'digging dragon'), also known as the snow drake, is a large reptillian pack predator similar in size and morphology to old Earth hyenas. They commonly dig shallow dens in dirt, snow or foliage, sometimes using them for concealment prior to an ambush. Biological cousins to the elusive kururak, they have heavy, low-slung bodies and powerful jaws suited to hunting land prey rather than fishing. Colonization and subsequent expansion have displaced many populations from their tundral territories into colder areas; as a result, their diet of Sivian prey animals has pivoted to a diet of giant spider meat. +

+Grafadrekas are capable of exerting bite pressures in excess of 900 PSI, which allows them to crack bones or carapace when scavenging for food. While they share the hypercarnivorous metabolism of their cousins, they have developed a symbiotic relationship with the bacteria responsible for the bioluminescence of Sivian trees. This assists with digesting plant matter, and gives their pelts a distinctive and eerie glow. +

+They have been observed to occasionally attack and kill colonists, generally when conditions are too poor to hunt their usual prey. Despite this, and despite their disposition being generally skittish and avoidant of colonists, some Sivian communities hold that they have been observed to guide or protect lost travellers. +

+Field studies suggest analytical abilities on par with some species of cepholapods, but their symbiotic physiology rapidly fails in captivity, making laboratory testing difficult. Their inability to make use of tools or form wider social groups beyond a handful of individuals has been hypothesised to prevent the expression of more complex social behaviors."} + value = CATALOGUER_REWARD_HARD + +/decl/mob_organ_names/grafadreka + hit_zones = list( + "head", + "chest", + "left foreleg", + "right foreleg", + "left hind leg", + "right hind leg", + "face spines", + "body spines", + "tail spines", + "tail" + ) + +/decl/emote/audible/drake_howl + key = "dhowl" + emote_message_3p = "lifts USER_THEIR head up and gives an eerie howl." + emote_sound = 'sound/effects/drakehowl_close.ogg' + broadcast_sound ='sound/effects/drakehowl_far.ogg' + emote_cooldown = 20 SECONDS + broadcast_distance = 90 + +/decl/emote/audible/drake_howl/broadcast_emote_to(var/send_sound, var/mob/target, var/direction) + if((. = ..())) + to_chat(target, SPAN_NOTICE("You hear an eerie howl from somewhere to the [dir2text(direction)].")) + +/mob/living/simple_mob/animal/sif/grafadreka/get_available_emotes() + if(!is_baby) + return global._default_mob_emotes | /decl/emote/audible/drake_howl + return global._default_mob_emotes + +// Overriding this to handle sitting. +/mob/living/simple_mob/animal/sif/grafadreka/lay_down() + . = ..() + if(!resting && sitting) + sitting = FALSE + update_icon() + +/mob/living/simple_mob/animal/sif/grafadreka/verb/sit_down() + set name = "Sit Down" + set category = "IC" + + if(sitting) + resting = FALSE + sitting = FALSE + else + resting = TRUE + sitting = TRUE + + to_chat(src, SPAN_NOTICE("You are now [sitting ? "sitting" : "getting up"].")) + update_canmove() + update_icon() + +/mob/living/simple_mob/animal/sif/grafadreka + name = "grafadreka" + desc = "A large, sleek snow drake with heavy claws, powerful jaws and many pale spines along its body." + player_msg = "You are a large Sivian pack predator in symbiosis with the local bioluminescent bacteria. You can eat glowing \ + tree fruit to fuel your ranged spitting attack and poisonous bite (on harm intent), as well as healing saliva \ + (on help intent).
There are humans moving through your territory; whether you help them get home safely, or treat them as a snack, \ + is up to you." + color = "#608894" + icon = 'icons/mob/drake_adult.dmi' + catalogue_data = list(/datum/category_item/catalogue/fauna/grafadreka) + icon_state = "doggo" + icon_living = "doggo" + icon_dead = "doggo_lying" + icon_rest = "doggo_lying" + projectileverb = "spits" + friendly = list("headbutts", "grooms", "play-bites", "rubs against") + bitesize = 10 // chomp + gender = NEUTER + + has_langs = list("Drake") + + see_in_dark = 8 // on par with Taj + + tt_desc = "S tesca pabulator" + faction = "grafadreka" + + mob_size = MOB_LARGE + mob_bump_flag = SIMPLE_ANIMAL + mob_swap_flags = SIMPLE_ANIMAL + mob_push_flags = SIMPLE_ANIMAL + + maxHealth = 150 + health = 150 + movement_cooldown = 2 + base_attack_cooldown = 1 SECOND + + organ_names = /decl/mob_organ_names/grafadreka + say_list_type = /datum/say_list/grafadreka + ai_holder_type = /datum/ai_holder/simple_mob/intentional/grafadreka + + scavenger = TRUE + burrower = TRUE + + projectilesound = 'sound/effects/splat.ogg' + projectiletype = /obj/item/projectile/drake_spit + + // Claw attacks. + attack_sharp = TRUE + melee_damage_lower = 8 + melee_damage_upper = 18 + attack_armor_pen = 15 + + attack_sound = 'sound/weapons/slice.ogg' + + tame_items = list( + /obj/item/reagent_containers/food/snacks/siffruit = 20, + /obj/item/reagent_containers/food/snacks/grown/sif/sifpod = 10, + /obj/item/reagent_containers/food/snacks/xenomeat/spidermeat = 20, + /obj/item/reagent_containers/food/snacks/meat = 10 + ) + + // Attack strings for swapping. + attacktext = null + var/static/list/claw_attacktext = list("slashed", "clawed", "swiped", "gouged") + var/static/list/bite_attacktext = list("savaged", "bitten", "mauled") + + // Bite attacks. + var/bite_melee_damage_lower = 30 + var/bite_melee_damage_upper = 40 + var/bite_attack_armor_pen = 60 + var/const/bite_attack_sound = 'sound/weapons/bite.ogg' + + // Used to avoid setting vars every single attack. + var/attacking_with_claws = TRUE + + // Set during initialize and used to generate overlays. + var/tmp/current_icon_state // used to track our 'actual' icon state due to overlay nonsense in update_icon + var/tmp/fur_colour + var/tmp/claw_colour + var/tmp/glow_colour + var/tmp/base_colour + var/tmp/eye_colour + + var/offset_compiled_icon = -16 + var/is_baby = FALSE + var/sitting = FALSE + var/next_spit = 0 + var/spit_cooldown = 8 SECONDS + var/next_leader_check = 0 + var/charisma = 0 // A score used to determine pack leader. + var/stored_sap = 0 + var/max_stored_sap = 60 + var/attacked_by_neutral = FALSE + + var/list/original_armor + +var/global/list/wounds_being_tended_by_drakes = list() +/mob/living/simple_mob/animal/sif/grafadreka/proc/can_tend_wounds(var/mob/living/friend) + + // We can't heal robots. + if(friend.isSynthetic()) + return FALSE + + // Check if someone else is looking after them already. + if(global.wounds_being_tended_by_drakes["\ref[friend]"] > world.time) + return FALSE + + // Humans need to have a bleeding external organ to qualify. + if(ishuman(friend)) + var/mob/living/carbon/human/H = friend + for(var/obj/item/organ/external/E in H.bad_external_organs) + if(E.status & ORGAN_BLEEDING) + return TRUE + return FALSE + + // Sif animals need to be able to regenerate past their current HP value. + if(istype(friend, /mob/living/simple_mob/animal/sif)) + var/mob/living/simple_mob/animal/sif/critter = friend + return critter.health < (critter.getMaxHealth() * critter.sap_heal_threshold) + + // Other animals just need to be injured. + return (friend.health < friend.maxHealth) + +/mob/living/simple_mob/animal/sif/grafadreka/Initialize() + + charisma = rand(5, 15) + stored_sap = rand(20, 30) + nutrition = rand(400,500) + + if(gender == NEUTER) + gender = pick(MALE, FEMALE) + attacktext = claw_attacktext.Copy() + + setup_colours() + create_reagents(50) + + . = ..() + + original_armor = armor + update_icon() + +/mob/living/simple_mob/animal/sif/grafadreka/examine(var/mob/living/user) + . = ..() + if(istype(user, /mob/living/simple_mob/animal/sif/grafadreka) || isobserver(user)) + var/datum/gender/G = gender_datums[get_visible_gender()] + if(stored_sap >= 20) + . += SPAN_NOTICE("[G.His] sap reserves are high.") + else if(stored_sap >= 10) + . += SPAN_WARNING("[G.His] sap reserves are running low.") + else + . += SPAN_DANGER("[G.His] sap reserves are depleted.") + +/mob/living/simple_mob/animal/sif/grafadreka/can_projectile_attack(var/atom/A) + if(a_intent != I_HURT || world.time < next_spit) + return FALSE + if(!has_sap(2)) + to_chat(src, SPAN_WARNING("You have no sap to spit!")) + return FALSE + return ..() + +// Checking this in the proc itself as AI doesn't seem to care about ranged attack cooldowns. +/mob/living/simple_mob/animal/sif/grafadreka/shoot_target(atom/A) + if(world.time < next_spit || !has_sap(2)) + return FALSE + . = ..() + if(.) + next_spit = world.time + spit_cooldown + setMoveCooldown(1 SECOND) + spend_sap(2) + +/mob/living/simple_mob/animal/sif/grafadreka/get_dietary_food_modifier(var/datum/reagent/nutriment/food) + if(food.allergen_type & ALLERGEN_MEAT) + return ..() + return 0.25 // Quarter nutrition from non-meat. + +/mob/living/simple_mob/animal/sif/grafadreka/handle_reagent_transfer(var/datum/reagents/holder, var/amount = 1, var/chem_type = CHEM_BLOOD, var/multiplier = 1, var/copy = 0) + return holder.trans_to_holder(reagents, amount, multiplier, copy) + +/mob/living/simple_mob/animal/sif/grafadreka/Life() + . = ..() + + if(stat == CONSCIOUS) + + // Don't make clientless drakes lose nutrition or they'll all go feral. + if(!resting && client) + remove_nutrition(0.3) + + // Very slowly regenerate enough sap to defend ourselves. spit is 2 sap, + // spit cooldown is 8s, life is 2s, so this is one free spit per 12 seconds. + if(stored_sap < 10) + add_sap(0.35) + + // Process food and sap chems. + if(reagents?.total_volume) + for(var/datum/reagent/chem in reagents.reagent_list) + var/removed = clamp(chem.ingest_met, REM, chem.volume) + chem.affect_animal(src, removed) + reagents.remove_reagent(chem.id, removed) + +/mob/living/simple_mob/animal/sif/grafadreka/proc/has_sap(var/amt) + return stored_sap >= amt + +/mob/living/simple_mob/animal/sif/grafadreka/proc/add_sap(var/amt) + stored_sap = clamp(round(stored_sap + amt, 0.01), 0, max_stored_sap) + update_icon() + return TRUE + +/mob/living/simple_mob/animal/sif/grafadreka/proc/spend_sap(var/amt) + if(has_sap(amt)) + stored_sap = clamp(round(stored_sap - amt, 0.01), 0, max_stored_sap) + update_icon() + return TRUE + return FALSE + +/mob/living/simple_mob/animal/sif/grafadreka/proc/setup_colours() + + var/static/list/fur_colours = list(COLOR_SILVER, COLOR_WHITE, COLOR_GREEN_GRAY, COLOR_PALE_RED_GRAY, COLOR_BLUE_GRAY) + var/static/list/claw_colours = list(COLOR_GRAY, COLOR_SILVER, COLOR_WHITE, COLOR_GRAY15, COLOR_GRAY20, COLOR_GRAY40, COLOR_GRAY80) + var/static/list/glow_colours = list(COLOR_BLUE_LIGHT, COLOR_LIGHT_CYAN, COLOR_CYAN, COLOR_CYAN_BLUE) + var/static/list/base_colours = list("#608894", "#436974", "#7fa3ae") + var/static/list/eye_colours = list(COLOR_WHITE, COLOR_SILVER) + + if(!glow_colour) + glow_colour = pick(glow_colours) + if(!fur_colour) + fur_colour = pick(fur_colours) + if(!claw_colour) + claw_colour = pick(claw_colours) + if(!base_colour) + base_colour = pick(base_colours) + if(!eye_colour) + eye_colour = pick(eye_colours) + +/mob/living/simple_mob/animal/sif/grafadreka/movement_delay(oldloc, direct) + . = ..() + if(istype(loc, /turf/space)) + return + var/health_slowdown_threshold = round(maxHealth * 0.65) + if(health < health_slowdown_threshold) + . += round(5 * (1-(health / health_slowdown_threshold)), 0.1) + var/nut_slowdown_threshold = round(max_nutrition * 0.65) + if(nutrition < nut_slowdown_threshold) + . += round(5 * (1-(nutrition / nut_slowdown_threshold)), 0.1) + +/mob/living/simple_mob/animal/sif/grafadreka/update_icon() + + . = ..() + + if(sitting && stat == CONSCIOUS) + icon_state = "[initial(icon_state)]_sitting" + + var/list/add_images = list() + var/image/I = image(icon, "[icon_state]") + I.color = base_colour + add_images += I + + I = image(icon, "[icon_state]-fur") + I.color = fur_colour + add_images += I + + I = image(icon, "[icon_state]-claws") + I.color = claw_colour + add_images += I + + if(stat == CONSCIOUS && !sleeping) + I = image(icon, "[icon_state]-eye_overlay") + I.color = eye_colour + add_images += I + + if(stat != DEAD) + var/glow = add_glow() + if(glow) + add_images += glow + + for(var/image/adding in add_images) + adding.appearance_flags |= (RESET_COLOR|PIXEL_SCALE|KEEP_APART) + if(offset_compiled_icon) + adding.pixel_x = offset_compiled_icon // Offset here so that things like modifiers, runechat text, etc. are centered + add_overlay(adding) + + // We do this last so the default mob icon_state can be used for the overlays. + current_icon_state = icon_state + icon_state = "blank" + color = COLOR_WHITE // Due to KEEP_TOGETHER etc. overlays ignore RESET_COLOR. + +/mob/living/simple_mob/animal/sif/grafadreka/proc/add_glow() + var/image/I = image(icon, "[icon_state]-glow") + I.color = glow_colour + I.plane = PLANE_LIGHTING_ABOVE + I.alpha = 35 + round(220 * clamp(stored_sap/max_stored_sap, 0, 1)) + return I + +/mob/living/simple_mob/animal/sif/grafadreka/get_eye_color() + return eye_colour + +/mob/living/simple_mob/animal/sif/grafadreka/do_tame(var/obj/O, var/mob/user) + . = ..() + attacked_by_neutral = FALSE + +/mob/living/simple_mob/animal/sif/grafadreka/handle_special() + ..() + if(client || world.time >= next_leader_check) + next_leader_check = world.time + (60 SECONDS) + check_leader_status() + +/mob/living/simple_mob/animal/sif/grafadreka/do_help_interaction(atom/A) + + if(isliving(A)) + + var/mob/living/friend = A + if(friend.stat == DEAD) + if(friend == src) + to_chat(src, SPAN_WARNING("\The [friend] is dead; tending their wounds is pointless.")) + else + return ..() + return TRUE + + if(!can_tend_wounds(friend)) + if(friend == src) + if(health == maxHealth) + to_chat(src, SPAN_WARNING("You are unwounded.")) + else + to_chat(src, SPAN_WARNING("You cannot tend any of your wounds.")) + else + if(friend.health == friend.maxHealth) + return ..() + to_chat(src, SPAN_WARNING("You cannot tend any of \the [friend]'s wounds.")) + return TRUE + + if(friend.has_modifier_of_type(/datum/modifier/sifsap_salve)) + if(friend == src) + to_chat(src, SPAN_WARNING("You have already cleaned your wounds.")) + else + return ..() + return TRUE + + if(!has_sap(10)) + if(friend == src) + to_chat(src, SPAN_WARNING("You don't have enough sap to clean your wounds.")) + else + return ..() + return TRUE + + if(friend == src) + visible_message(SPAN_NOTICE("\The [src] begins to drool a blue-glowing liquid, which they start slathering over their wounds.")) + else + visible_message(SPAN_NOTICE("\The [src] begins to drool a blue-glowing liquid, which they start slathering over \the [friend]'s wounds.")) + + playsound(src, 'sound/effects/ointment.ogg', 25) + + var/friend_ref = "\ref[friend]" + global.wounds_being_tended_by_drakes[friend_ref] = world.time + (8 SECONDS) + set_AI_busy(TRUE) + + if(!do_after(src, 8 SECONDS, friend) || QDELETED(friend) || friend.has_modifier_of_type(/datum/modifier/sifsap_salve) || incapacitated() || !spend_sap(10)) + global.wounds_being_tended_by_drakes -= friend_ref + set_AI_busy(FALSE) + return TRUE + + global.wounds_being_tended_by_drakes -= friend_ref + set_AI_busy(FALSE) + + if(friend == src) + visible_message(SPAN_NOTICE("\The [src] finishes licking at their wounds.")) + else + visible_message(SPAN_NOTICE("\The [src] finishes licking at \the [friend]'s wounds.")) + playsound(src, 'sound/effects/ointment.ogg', 25) + + // Sivian animals get a heal buff from the modifier, others just + // get it to stop friendly drakes constantly licking their wounds. + friend.add_modifier(/datum/modifier/sifsap_salve, 60 SECONDS) + // Human wounds are closed, but they get sifsap via open wounds. + if(ishuman(friend)) + var/mob/living/carbon/human/H = friend + for(var/obj/item/organ/external/E in H.organs) + if(E.status & ORGAN_BLEEDING) + E.organ_clamp() + H.bloodstr.add_reagent("sifsap", rand(1,2)) + for(var/datum/wound/W in E.wounds) + W.salve() + W.disinfect() + + // Everyone else is just poisoned. + else if(!istype(friend, /mob/living/simple_mob/animal/sif)) + friend.adjustToxLoss(rand(10,20)) + return TRUE + + return ..() + +/mob/living/simple_mob/animal/sif/grafadreka/proc/get_pack_leader() + var/pack = FALSE + var/mob/living/simple_mob/animal/sif/grafadreka/leader + if(!is_baby) + leader = src + for(var/mob/living/simple_mob/animal/sif/grafadreka/follower in hearers(7, loc)) + if(follower == src || follower.is_baby || follower.stat == DEAD || follower.faction != faction) + continue + pack = TRUE + if(!leader || follower.charisma > leader.charisma) + leader = follower + if(pack) + return leader + +/mob/living/simple_mob/animal/sif/grafadreka/proc/check_leader_status() + var/mob/living/simple_mob/animal/sif/grafadreka/leader = get_pack_leader() + if(src == leader) + add_modifier(/datum/modifier/ace, 60 SECONDS) + else + remove_modifiers_of_type(/datum/modifier/ace) + +/mob/living/simple_mob/animal/sif/grafadreka/Stat() + . = ..() + if(statpanel("Status")) + stat("Nutrition:", "[nutrition]/[max_nutrition]") + stat("Stored sap:", "[stored_sap]/[max_stored_sap]") + +/mob/living/simple_mob/animal/sif/grafadreka/proc/can_bite(var/mob/living/M) + return istype(M) && (M.lying || M.confused || M.incapacitated()) + +/mob/living/simple_mob/animal/sif/grafadreka/apply_bonus_melee_damage(atom/A, damage_amount) + // Melee attack on incapacitated or prone enemies bites instead of slashing + var/last_attack_was_claws = attacking_with_claws + attacking_with_claws = !can_bite(A) + + if(last_attack_was_claws != attacking_with_claws) + if(attacking_with_claws) // Use claws. + attack_armor_pen = initial(attack_armor_pen) + attack_sound = initial(attack_sound) + attacktext = claw_attacktext.Copy() + else // Use ur teef + damage_amount = max(damage_amount, rand(bite_melee_damage_lower, bite_melee_damage_upper)) + attack_armor_pen = bite_attack_armor_pen + attack_sound = bite_attack_sound + attacktext = bite_attacktext.Copy() + . = ..() + +// Eating sifsap makes bites toxic and changes our glow intensity. +/mob/living/simple_mob/animal/sif/grafadreka/apply_attack(atom/A, damage_to_do) + var/tox_damage = 0 + if(!attacking_with_claws && isliving(A) && has_sap(5)) + tox_damage = rand(5,15) + . = ..() + if(. && tox_damage && spend_sap(5)) + var/mob/living/M = A + M.adjustToxLoss(tox_damage) + +/mob/living/simple_mob/animal/sif/grafadreka/verb/rally_pack() + set name = "Rally Pack" + set desc = "Tries to command your fellow pack members to follow you." + set category = "Abilities" + + if(!has_modifier_of_type(/datum/modifier/ace)) + to_chat(src, SPAN_WARNING("You aren't the pack leader! Sit down!")) + return + + audible_message("\The [src] barks loudly and rattles its neck spines.") + for(var/mob/living/simple_mob/animal/sif/grafadreka/drake in hearers(world.view * 3, src)) + if(drake == src || drake.faction != faction) + continue + if(drake.client) + to_chat(drake, SPAN_NOTICE("The pack leader wishes for you to follow them.")) + else if(drake.ai_holder) + drake.ai_holder.set_follow(src) + +/mob/living/simple_mob/animal/sif/grafadreka/has_appetite() + return reagents && abs(reagents.total_volume - reagents.maximum_volume) >= 10 + +/mob/living/simple_mob/animal/sif/grafadreka/Login() + . = ..() + charisma = (client && !is_baby) ? INFINITY : 0 + +/mob/living/simple_mob/animal/sif/grafadreka/Logout() + . = ..() + if(!client) + charisma = rand(5, 15) + +/datum/say_list/grafadreka + speak = list("Chff!","Skhh.", "Rrrss...") + emote_see = list("scratches its ears","grooms its spines", "sways its tail", "claws at the ground") + emote_hear = list("hisses", "rattles", "rasps", "barks") + +/obj/structure/animal_den/ghost_join/grafadreka + name = "drake den" + critter = /mob/living/simple_mob/animal/sif/grafadreka + +/obj/structure/animal_den/ghost_join/grafadreka_hatchling + name = "drake hatchling den" + critter = /mob/living/simple_mob/animal/sif/grafadreka/hatchling + +// Subtypes! +/mob/living/simple_mob/animal/sif/grafadreka/rainbow/setup_colours() + glow_colour = get_random_colour(TRUE) + fur_colour = get_random_colour(TRUE) + claw_colour = get_random_colour(TRUE) + base_colour = get_random_colour(TRUE) + eye_colour = get_random_colour(TRUE) + ..() + +/mob/living/simple_mob/animal/sif/grafadreka/hatchling + name = "grafadreka hatchling" + icon = 'icons/mob/drake_baby.dmi' + mob_size = MOB_SMALL + desc = "An immature snow drake, not long out of the shell." + is_baby = TRUE + offset_compiled_icon = null + + melee_damage_lower = 3 + melee_damage_upper = 5 + attack_armor_pen = 2 + bite_melee_damage_lower = 5 + bite_melee_damage_upper = 10 + bite_attack_armor_pen = 16 + + projectiletype = /obj/item/projectile/drake_spit/weak + maxHealth = 60 + health = 60 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm index 4509634757..07231caf07 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm @@ -5,7 +5,7 @@ Scour its code if you dare. Here's a summary, however. -This is a 128x64px mob with sprites drawn by Przyjaciel (thanks mate) and some codersprites. +This is a 128x92px mob with sprites drawn by Przyjaciel (thanks mate) and some codersprites. The bigdragon is an 800 health hostile boss mob with three special attacks. The first (disarm intent) is a charge attack that activates when the target is >5 tiles away and requires line of sight. @@ -62,6 +62,7 @@ I think I covered everything. meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat old_x = -48 old_y = 0 + vis_height = 92 melee_damage_lower = 35 melee_damage_upper = 25 melee_miss_chance = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/greatwolf.dm b/code/modules/mob/living/simple_mob/subtypes/vore/greatwolf.dm index 41098bc65c..e814e3e93a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/greatwolf.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/greatwolf.dm @@ -14,6 +14,7 @@ meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat old_x = -48 old_y = 0 + vis_height = 92 melee_damage_lower = 20 melee_damage_upper = 15 friendly = list("nudges", "sniffs on", "rumbles softly at", "slobberlicks") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander.dm b/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander.dm index 2b4350fd2e..5427b978c9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander.dm @@ -18,6 +18,7 @@ default_pixel_x = -48 pixel_x = -48 pixel_y = 0 + vis_height = 92 response_help = "pats" response_disarm = "shoves" response_harm = "bops" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander_ch.dm b/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander_ch.dm index 4f7e9e6bed..d8fb0f212f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander_ch.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander_ch.dm @@ -12,6 +12,7 @@ meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat old_x = -48 old_y = 0 + vis_height = 92 melee_damage_lower = 10 melee_damage_upper = 25 friendly = list("nudges", "sniffs on", "rumbles softly at", "slobberlicks") diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index c36a48f13f..0a4e270ed6 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -408,7 +408,7 @@ var/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HURT) return // Can't talk in deadchat if you can't see it. for(var/mob/M in player_list) - if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights)) && M.is_preference_enabled(/datum/client_preference/show_dsay)) + if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights && M.is_preference_enabled(/datum/client_preference/holder/show_staff_dsay))) && M.is_preference_enabled(/datum/client_preference/show_dsay)) var/follow var/lname if(M.forbid_seeing_deadchat && !M.client.holder) @@ -438,7 +438,7 @@ var/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HURT) /proc/say_dead_object(var/message, var/obj/subject = null) for(var/mob/M in player_list) - if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights)) && M.is_preference_enabled(/datum/client_preference/show_dsay)) + if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights && M.is_preference_enabled(/datum/client_preference/holder/show_staff_dsay))) && M.is_preference_enabled(/datum/client_preference/show_dsay)) var/follow var/lname = "Game Master" if(M.forbid_seeing_deadchat && !M.client.holder) diff --git a/code/modules/mob/new_player/sprite_accessories_tail_vr.dm b/code/modules/mob/new_player/sprite_accessories_tail_vr.dm index 4f267fe7de..cd63d811ef 100644 --- a/code/modules/mob/new_player/sprite_accessories_tail_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_tail_vr.dm @@ -1185,6 +1185,13 @@ color_blend_mode = ICON_MULTIPLY lower_layer_dirs = list(SOUTH, WEST) +/datum/sprite_accessory/tail/shark_finless + name = "shark tail, finless (colorable)" + desc = "" + icon_state = "sharktail_finless" + do_colouration = 1 + color_blend_mode = ICON_MULTIPLY + /datum/sprite_accessory/tail/tentacle name = "Tentacle, colorable (vwag)" desc = "" diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index 11ce2fbb45..60498402ef 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -237,7 +237,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) return 0 for(var/modifier_type in R.genetic_modifiers) //Can't be revived. Probably won't happen...? - if(istype(modifier_type, /datum/modifier/no_clone)) + if(ispath(modifier_type, /datum/modifier/no_clone)) return 0 var/mob/living/carbon/human/H = new /mob/living/carbon/human(get_turf(src), R.dna.species) diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index c91fa50481..718234893e 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -368,6 +368,9 @@ var/list/organ_cache = list() robotize() /obj/item/organ/emp_act(severity) + for(var/obj/O as anything in src.contents) + O.emp_act(severity) + if(!(robotic >= ORGAN_ASSISTED)) return for(var/i = 1; i <= robotic; i++) diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 63dc8c6666..912889c5f6 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -115,6 +115,9 @@ return ..() /obj/item/organ/external/emp_act(severity) + for(var/obj/O as anything in src.contents) + O.emp_act(severity) + if(!(robotic >= ORGAN_ROBOT)) return var/burn_damage = 0 @@ -595,8 +598,6 @@ This function completely restores a damaged organ to perfect condition. return 1 else last_dam = brute_dam + burn_dam - if (number_wounds != 0) - return 1 if(germ_level) return 1 return 0 @@ -733,8 +734,8 @@ Note that amputating the affected organ does in fact remove the infection from t return for(var/datum/wound/W in wounds) - // wounds used to be able to disappear after 10 minutes at the earliest, for now just remove them as soon as there is no damage - if(W.damage <= 0) + // wounds can disappear after 10 minutes at the earliest + if(W.damage <= 0 && W.created + 10 MINUTES <= world.time) wounds -= W continue // let the GC handle the deletion of the wound diff --git a/code/modules/organs/pain.dm b/code/modules/organs/pain.dm index 6cf236bc19..b65b3fc2fb 100644 --- a/code/modules/organs/pain.dm +++ b/code/modules/organs/pain.dm @@ -9,7 +9,7 @@ // power decides how much painkillers will stop the message // force means it ignores anti-spam timer /mob/living/carbon/proc/custom_pain(message, power, force) - if(!message || stat || !can_feel_pain() || chem_effects[CE_PAINKILLER] > power) + if((!message || stat || !can_feel_pain() || chem_effects[CE_PAINKILLER] > power) && !synth_cosmetic_pain) return 0 message = "[message]" if(power >= 50) @@ -25,7 +25,7 @@ if(stat) return - if(!can_feel_pain()) + if(!can_feel_pain() && !synth_cosmetic_pain) return if(world.time < next_pain_time) @@ -33,7 +33,7 @@ var/maxdam = 0 var/obj/item/organ/external/damaged_organ = null for(var/obj/item/organ/external/E in organs) - if(!E.organ_can_feel_pain()) continue + if(!E.organ_can_feel_pain() && !synth_cosmetic_pain) continue var/dam = E.get_damage() // make the choice of the organ depend on damage, // but also sometimes use one of the less damaged ones diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 3fd25a83e4..d4c8a8bf59 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -143,7 +143,7 @@ GLOBAL_LIST_EMPTY(apcs) var/updating_icon = 0 var/global/list/status_overlays_environ var/alarms_hidden = FALSE //If power alarms from this APC are visible on consoles - + var/nightshift_lights = FALSE var/nightshift_setting = NIGHTSHIFT_AUTO var/last_nightshift_switch = 0 @@ -198,7 +198,7 @@ GLOBAL_LIST_EMPTY(apcs) if(!pixel_x && !pixel_y) offset_apc() - + if(building) area = get_area(src) area.apc = src @@ -1360,6 +1360,7 @@ GLOBAL_LIST_EMPTY(apcs) for(var/obj/machinery/light/L in area) L.nightshift_mode(new_state) + L.update() //For some reason it gets hung up on updating the overlay for the light fixture somewhere down the line. This fixes it. CHECK_TICK #undef APC_UPDATE_ICON_COOLDOWN diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 52e412b0ee..e1b41b602e 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -215,6 +215,7 @@ var/global/list/light_type_cache = list() idle_power_usage = 2 active_power_usage = 10 power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list + var/obj/item/weapon/light/installed_light //What light is currently in the socket! Updated in new() var/on = 0 // 1 if on, 0 if off var/brightness_range var/brightness_power @@ -344,9 +345,10 @@ var/global/list/light_type_cache = list() construct.transfer_fingerprints_to(src) set_dir(construct.dir) else + installed_light = new light_type(src) if(start_with_cell && !no_emergency) cell = new/obj/item/weapon/cell/emergency_light(src) - var/obj/item/weapon/light/L = get_light_type_instance(light_type) + var/obj/item/weapon/light/L = get_light_type_instance(light_type) //This is fine, but old code. update_from_bulb(L) if(prob(L.broken_chance)) broken(1) @@ -439,7 +441,7 @@ var/global/list/light_type_cache = list() return current_alert = null - var/obj/item/weapon/light/L = get_light_type_instance(light_type) + var/obj/item/weapon/light/L = installed_light //This ensures any special bulbs will stay special! if(L) update_from_bulb(L) @@ -464,11 +466,12 @@ var/global/list/light_type_cache = list() var/correct_range = nightshift_enabled ? brightness_range_ns : brightness_range var/correct_power = nightshift_enabled ? brightness_power_ns : brightness_power var/correct_color = nightshift_enabled ? brightness_color_ns : brightness_color + var/correct_overlay = nightshift_enabled ? brightness_color_ns : brightness_color //Gives lights the correct overlay if NS is enabled. if(current_alert) //Oh no, we're on fire! Or the atmos is bad! Let's change the color correct_range = brightness_range correct_power = brightness_power correct_color = brightness_color - if(light_range != correct_range || light_power != correct_power || light_color != correct_color) + if(light_range != correct_range || light_power != correct_power || light_color != correct_color || overlay_color != correct_overlay) if(!auto_flicker) switchcount++ if(rigged) @@ -487,6 +490,7 @@ var/global/list/light_type_cache = list() else update_use_power(USE_POWER_ACTIVE) set_light(correct_range, correct_power, correct_color) + overlay_color = correct_overlay if(cell?.charge < cell?.maxcharge) START_PROCESSING(SSobj, src) else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE) && !turned_off()) @@ -571,6 +575,7 @@ var/global/list/light_type_cache = list() brightness_range = L.brightness_range brightness_power = L.brightness_power brightness_color = L.brightness_color + overlay_color = L.brightness_color brightness_range_ns = L.nightshift_range brightness_power_ns = L.nightshift_power @@ -580,7 +585,8 @@ var/global/list/light_type_cache = list() /obj/machinery/light/proc/insert_bulb(obj/item/weapon/light/L) update_from_bulb(L) - qdel(L) + installed_light = L + L.loc = src //Move it into the socket! on = powered() update() @@ -593,16 +599,17 @@ var/global/list/light_type_cache = list() explode() /obj/machinery/light/proc/remove_bulb() - . = new light_type(src.loc, src) + //. = new light_type(src.loc, src) switchcount = 0 + installed_light = null status = LIGHT_EMPTY update() /obj/machinery/light/attackby(obj/item/W, mob/user) //Light replacer code - if(istype(W, /obj/item/device/lightreplacer)) + if(istype(W, /obj/item/device/lightreplacer)) //These will never be modified, so it's fine to use old code. var/obj/item/device/lightreplacer/LR = W if(isliving(user)) var/mob/living/U = user @@ -619,7 +626,9 @@ var/global/list/light_type_cache = list() return to_chat(user, "You insert [W].") + user.drop_item() insert_bulb(W) + update() //Like other places, this is done later down the line but this is essential to updating the overlay when nightmode is involved. Again, I have no idea WHY. src.add_fingerprint(user) // attempt to break the light @@ -724,6 +733,7 @@ var/global/list/light_type_cache = list() if(cell.charge > 300) //it's meant to handle 120 W, ya doofus visible_message("[src] short-circuits from too powerful of a power cell!") status = LIGHT_BURNED + installed_light.status = status return FALSE cell.use(pwr) set_light(brightness_range * bulb_emergency_brightness_mul, max(bulb_emergency_pow_min, bulb_emergency_pow_mul * (cell.charge / cell.maxcharge)), bulb_emergency_colour) @@ -805,8 +815,11 @@ var/global/list/light_type_cache = list() else to_chat(user, "You remove the light [get_fitting_name()].") - // create a light tube/bulb item and put it in the user's hand - user.put_in_active_hand(remove_bulb()) //puts it in our active hand + //Let's actually put the real bulb in their hand. + installed_light.status = status //Update the bulb they're being given. If it's broken, the bulb should be as well! + user.put_in_active_hand(installed_light) //puts it in our active hand + installed_light.update_icon() + remove_bulb() /obj/machinery/light/flamp/attack_hand(mob/user) if(lamp_shade) @@ -845,13 +858,17 @@ var/global/list/light_type_cache = list() var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(3, 1, src) s.start() - status = LIGHT_BROKEN + status = LIGHT_BROKEN //This occasionally runtimes when it occurs midround after build mode spawns a broken light. No idea why. + installed_light.status = status + installed_light.update_icon() update() /obj/machinery/light/proc/fix() if(status == LIGHT_OK) return status = LIGHT_OK + if(installed_light) + installed_light.status = LIGHT_OK on = 1 update() diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index e4d3254fda..9ccf421b57 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -184,8 +184,8 @@ /obj/machinery/particle_accelerator/control_box/proc/toggle_power() active = !active investigate_log("turned [active?"ON":"OFF"] by [usr ? usr.key : "outside forces"]","singulo") - message_admins("PA Control Computer turned [active ?"ON":"OFF"] by [key_name(usr, usr.client)](?) in ([x],[y],[z] - JMP)",0,1) - log_game("PACCEL([x],[y],[z]) [key_name(usr)] turned [active?"ON":"OFF"].") + message_admins("PA Control Computer turned [active ?"ON":"OFF"] by [usr ? key_name(usr, usr.client) : "outside forces"](?) in ([x],[y],[z] - JMP)",0,1) + log_game("PACCEL([x],[y],[z]) [usr ? key_name(usr, usr.client) : "outside forces"] turned [active?"ON":"OFF"].") if(active) update_use_power(USE_POWER_ACTIVE) for(var/obj/structure/particle_accelerator/part in connected_parts) diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm index 3b95676eee..22dd450169 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm @@ -382,13 +382,6 @@ . = TRUE if(src in KA.modkits) // Sanity check to prevent installing the same modkit twice thanks to occasional click/lag delays. return FALSE - // if(minebot_upgrade) - // if(minebot_exclusive && !istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone)) - // to_chat(user, "The modkit you're trying to install is only rated for minebot use.") - // return FALSE - // else if(istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone)) - // to_chat(user, "The modkit you're trying to install is not rated for minebot use.") - // return FALSE if(denied_type) var/number_of_denied = 0 for(var/A in KA.get_modkits()) diff --git a/code/modules/rogueminer_vr/controller.dm b/code/modules/rogueminer_vr/controller.dm index 4c6c710120..efff235f2e 100644 --- a/code/modules/rogueminer_vr/controller.dm +++ b/code/modules/rogueminer_vr/controller.dm @@ -197,6 +197,6 @@ var/datum/controller/rogue/rm_controller rm_controller.dbg("RMC(pnz): Cleaning up oldest zone.") spawn(0) //Detatch it so we can return the new zone for now. var/datum/rogue/zonemaster/ZM_oldest = get_oldest_zone() - ZM_oldest.clean_zone() + if(ZM_oldest) ZM_oldest.clean_zone() return ZM_target \ No newline at end of file diff --git a/code/modules/tgui/states/notcontained.dm b/code/modules/tgui/states/notcontained.dm index 01811d427f..56b789e3c0 100644 --- a/code/modules/tgui/states/notcontained.dm +++ b/code/modules/tgui/states/notcontained.dm @@ -21,6 +21,3 @@ GLOBAL_DATUM_INIT(tgui_notcontained_state, /datum/tgui_state/notcontained_state, /mob/living/silicon/notcontained_can_use_tgui_topic(src_object) return default_can_use_tgui_topic(src_object) // Silicons use default bevhavior. - -/mob/living/simple_animal/drone/notcontained_can_use_tgui_topic(src_object) - return default_can_use_tgui_topic(src_object) // Drones use default bevhavior. diff --git a/code/modules/vchat/html/vchat.html b/code/modules/vchat/html/vchat.html index a3612d78e4..5b7c7b4f97 100644 --- a/code/modules/vchat/html/vchat.html +++ b/code/modules/vchat/html/vchat.html @@ -11,9 +11,9 @@ - + - + @@ -32,7 +32,7 @@
{{tab_unread_count(tab)}}
- + - + - +
@@ -175,7 +175,7 @@ shown_messages.length: {{shown_messages.length}}
--> - +
diff --git a/code/modules/vchat/js/polyfills.min.js b/code/modules/vchat/js/polyfills.min.js new file mode 100644 index 0000000000..7acecca62b --- /dev/null +++ b/code/modules/vchat/js/polyfills.min.js @@ -0,0 +1 @@ +function storageAvailable(e){var t;try{t=window[e];var r="__storage_test__";return t.setItem(r,r),t.removeItem(r),!0}catch(e){return e instanceof DOMException&&(22===e.code||1014===e.code||"QuotaExceededError"===e.name||"NS_ERROR_DOM_QUOTA_REACHED"===e.name)&&t&&0!==t.length}}Array.prototype.find||Object.defineProperty(Array.prototype,"find",{value:function(e){if(null==this)throw TypeError('"this" is null or not defined');var t=Object(this),r=t.length>>>0;if("function"!=typeof e)throw TypeError("predicate must be a function");for(var n=arguments[1],o=0;o= 8 && href.substring(0,8) == "byond://")) { window.location = href; //Internal byond link } else { //It's an external link @@ -676,7 +676,7 @@ function start_vue() { }, save_chatlog: function() { var textToSave = ""; - + var messagesToSave = this.archived_messages.concat(this.messages); messagesToSave.forEach( function(message) { @@ -751,7 +751,7 @@ function check_ping() { function send_latency_check() { if(vchat_state.latency_sent) return; - + vchat_state.latency_sent = Date.now(); vueapp.latency = "?"; push_Topic("ping"); @@ -776,10 +776,10 @@ function get_latency_check() { //We accept double-url-encoded JSON strings because Byond is garbage and UTF-8 encoded url_encode() text has crazy garbage in it. function byondDecode(message) { - + //Byond encodes spaces as pluses?! This is 1998 I guess. message = message.replace(/\+/g, "%20"); - try { + try { message = decodeURIComponent(message); } catch (err) { message = unescape(message); @@ -839,7 +839,7 @@ function get_event(event) { case 'internal_error': system_message("Event parse error: " + event); break; - + //They provided byond data. case 'byond_player': send_client_data(); @@ -867,8 +867,8 @@ function get_event(event) { case 'availability': push_Topic("done_loading"); break; - - default: + + default: system_message("Didn't know what to do with event: " + event); } } @@ -892,7 +892,7 @@ function set_localstorage(key, value) { function get_localstorage(key, deffo) { let localstorage = window.localStorage; let value = localstorage.getItem(vchat_opts.cookiePrefix+key); - + //localstorage only stores strings. if(value === "null" || value === null) { value = deffo; @@ -944,7 +944,7 @@ function get_cookie(key, deffo) { var SKIN_BUTTONS = [ /* Rpane */ "rpane.textb", "rpane.infob", "rpane.wikib", "rpane.forumb", "rpane.rulesb", "rpane.github", "rpane.discord", "rpane.mapb", "rpane.changelog", /* Mainwindow */ "mainwindow.saybutton", "mainwindow.mebutton", "mainwindow.hotkey_toggle" - + ]; // Windows or controls that need background-color set. var SKIN_ELEMENTS = [ diff --git a/code/modules/vchat/js/vchat.min.js b/code/modules/vchat/js/vchat.min.js new file mode 100644 index 0000000000..8dce654de0 --- /dev/null +++ b/code/modules/vchat/js/vchat.min.js @@ -0,0 +1 @@ +!function(){var e=console.log;console.log=function(t){send_debug(t),e.apply(console,arguments)};var t=console.error;console.error=function(e){send_debug(e),t.apply(console,arguments)},window.onerror=function(e,t,s,a,n){var o="";return n&&n.stack&&(o=n.stack),send_debug(e+" ("+t+"@"+s+":"+a+") "+n+"|UA: "+navigator.userAgent+"|Stack: "+o),!0}}();var vchat_opts={msBeforeDropped:3e4,cookiePrefix:"vst-",alwaysShow:["vc_looc","vc_system"],vchatTabsVer:1},DARKMODE_COLORS={buttonBgColor:"#40628a",buttonTextColor:"#FFFFFF",windowBgColor:"#272727",highlightColor:"#009900",tabTextColor:"#FFFFFF",tabBackgroundColor:"#272727"},LIGHTMODE_COLORS={buttonBgColor:"none",buttonTextColor:"#000000",windowBgColor:"none",highlightColor:"#007700",tabTextColor:"#000000",tabBackgroundColor:"none"},set_storage=set_cookie,get_storage=get_cookie,domparser=new DOMParser;storageAvailable("localStorage")&&(set_storage=set_localstorage,get_storage=get_localstorage);var vueapp,vchat_state={ready:!1,byond_ip:null,byond_cid:null,byond_ckey:null,lastPingReceived:0,latency_sent:0,lastId:0};function start_vchat(){start_vue(),vchat_state.ready=!0,push_Topic("done_loading"),push_Topic_showingnum(this.showingnum),doWinset("htmloutput",{"is-visible":!0}),doWinset("oldoutput",{"is-visible":!1}),doWinset("chatloadlabel",{"is-visible":!1}),setInterval(check_ping,vchat_opts.msBeforeDropped),send_debug("VChat Loaded!")}function start_vue(){vueapp=new Vue({el:"#app",data:{messages:[],shown_messages:[],unshown_messages:0,archived_messages:[],tabs:[{name:"Main",categories:[],immutable:!0,active:!0}],unread_messages:{},editing:!1,paused:!1,latency:0,reconnecting:!1,ext_styles:"",is_admin:!1,inverted:!1,crushing:3,animated:!1,fontsize:.9,lineheight:130,showingnum:200,type_table:[{matches:".filter_say, .say, .emote, .emote_subtle",becomes:"vc_localchat",pretty:"Local Chat",tooltip:"In-character local messages (say, emote, etc)",required:!1,admin:!1},{matches:".filter_radio, .alert, .syndradio, .centradio, .airadio, .entradio, .comradio, .secradio, .engradio, .medradio, .sciradio, .supradio, .srvradio, .expradio, .radio, .deptradio, .newscaster",becomes:"vc_radio",pretty:"Radio Comms",tooltip:"All departments of radio messages",required:!1,admin:!1},{matches:".filter_notice, .notice:not(.pm), .adminnotice, .info, .sinister, .cult",becomes:"vc_info",pretty:"Notices",tooltip:"Non-urgent messages from the game and items",required:!1,admin:!1},{matches:".filter_warning, .warning:not(.pm), .critical, .userdanger, .italics",becomes:"vc_warnings",pretty:"Warnings",tooltip:"Urgent messages from the game and items",required:!1,admin:!1},{matches:".filter_deadsay, .deadsay",becomes:"vc_deadchat",pretty:"Deadchat",tooltip:"All of deadchat",required:!1,admin:!1},{matches:".filter_ooc, .ooc:not(.looc)",becomes:"vc_globalooc",pretty:"Global OOC",tooltip:"The bluewall of global OOC messages",required:!1,admin:!1},{matches:".nif",becomes:"vc_nif",pretty:"NIF Messages",tooltip:"Messages from the NIF itself and people inside",required:!1,admin:!1},{matches:".mentor_channel, .mentor",becomes:"vc_mentor",pretty:"Mentor messages",tooltip:"Mentorchat and mentor pms",required:!1,admin:!1},{matches:".filter_pm, .pm",becomes:"vc_adminpm",pretty:"Admin PMs",tooltip:"Messages to/from admins ('adminhelps')",required:!1,admin:!1},{matches:".filter_ASAY, .admin_channel",becomes:"vc_adminchat",pretty:"Admin Chat",tooltip:"ASAY messages",required:!1,admin:!0},{matches:".filter_MSAY, .mod_channel",becomes:"vc_modchat",pretty:"Mod Chat",tooltip:"MSAY messages",required:!1,admin:!0},{matches:".filter_ESAY, .event_channel",becomes:"vc_eventchat",pretty:"Event Chat",tooltip:"ESAY messages",required:!1,admin:!0},{matches:".filter_combat, .danger",becomes:"vc_combat",pretty:"Combat Logs",tooltip:"Urist McTraitor has stabbed you with a knife!",required:!1,admin:!1},{matches:".filter_adminlogs, .log_message",becomes:"vc_adminlogs",pretty:"Admin Logs",tooltip:"ADMIN LOG: Urist McAdmin has jumped to coordinates X, Y, Z",required:!1,admin:!0},{matches:".filter_attacklogs",becomes:"vc_attacklogs",pretty:"Attack Logs",tooltip:"Urist McTraitor has shot John Doe",required:!1,admin:!0},{matches:".filter_debuglogs",becomes:"vc_debuglogs",pretty:"Debug Logs",tooltip:"DEBUG: SSPlanets subsystem Recover().",required:!1,admin:!0},{matches:".ooc.looc, .ooc, .looc",becomes:"vc_looc",pretty:"Local OOC",tooltip:"Local OOC messages, always enabled",required:!0},{matches:".boldannounce, .filter_system",becomes:"vc_system",pretty:"System Messages",tooltip:"Messages from your client, always enabled",required:!0}]},mounted:function(){this.load_settings();var e=new XMLHttpRequest;e.open("GET","ss13styles.css"),e.onreadystatechange=function(){this.ext_styles=e.responseText}.bind(this),e.send()},updated:function(){this.editing||this.paused||window.scrollTo(0,document.getElementById("messagebox").scrollHeight)},watch:{reconnecting:function(e,t){1==e&&0==t?this.internal_message("Your client has lost connection to the server, or there is severe lag. Your client will reconnect if possible."):0==e&&1==t&&this.internal_message("Your client has reconnected to the server.")},inverted:function(e){set_storage("darkmode",e),e?(document.body.classList.add("inverted"),switch_ui_mode(DARKMODE_COLORS)):(document.body.classList.remove("inverted"),switch_ui_mode(LIGHTMODE_COLORS))},crushing:function(e){set_storage("crushing",e)},animated:function(e){set_storage("animated",e)},fontsize:function(e,t){isNaN(e)?this.fontsize=t:(e<.2?this.fontsize=.2:e>5&&(this.fontsize=5),set_storage("fontsize",e))},lineheight:function(e,t){isFinite(e)?(e<100?this.lineheight=100:e>200&&(this.lineheight=200),set_storage("lineheight",e)):this.lineheight=t},showingnum:function(e,t){isFinite(e)?((e=Math.floor(e))<50?this.showingnum=50:e>2e3&&(this.showingnum=2e3),set_storage("showingnum",this.showingnum),push_Topic_showingnum(this.showingnum),this.attempt_archive()):this.showingnum=t},current_categories:function(e,t){e.length&&this.apply_filter(e)}},computed:{active_tab:function(){return this.tabs.find((function(e){return e.active}))},ping_classes:function(){return this.latency?"?"==this.latency?"grey":this.latency<0?"red":this.latency<=200?"green":this.latency<=400?"yellow":"grey":this.reconnecting?"red":"green"},current_categories:function(){return this.active_tab==this.tabs[0]?[]:this.active_tab.categories.concat(vchat_opts.alwaysShow)}},methods:{load_settings:function(){this.inverted=get_storage("darkmode",!1),this.crushing=get_storage("crushing",3),this.animated=get_storage("animated",!1),this.fontsize=get_storage("fontsize",.9),this.lineheight=get_storage("lineheight",130),this.showingnum=get_storage("showingnum",200),isNaN(this.crushing)&&(this.crushing=3),isNaN(this.fontsize)&&(this.fontsize=.9),this.load_tabs()},load_tabs:function(){var e=get_storage("tabs");if(e){var t=JSON.parse(e);t.version&&t.tabs?!t.version!=vchat_opts.vchatTabsVer?this.tabs.push.apply(this.tabs,t.tabs):this.internal_message("Your saved tabs are for an older version of VChat and must be recreated, sorry."):this.internal_message("There was a problem loading your tabs. Any new ones you make will be saved, however.")}},save_tabs:function(){var e={version:vchat_opts.vchatTabsVer,tabs:[]};this.tabs.forEach((function(t){if(!t.immutable){var s=t.name,a=[];t.categories.forEach((function(e){a.push(e)}));var n={name:s,categories:a,immutable:!1,active:!1};e.tabs.push(n)}}));var t=JSON.stringify(e);set_storage("tabs",t)},switchtab:function(e){e!=this.active_tab&&(this.active_tab.active=!1,e.active=!0,e.categories.forEach((function(e){this.unread_messages[e]=0}),this),this.apply_filter(this.current_categories))},editmode:function(){this.editing=!this.editing,this.save_tabs()},pause:function(){this.paused=!this.paused},newtab:function(){this.tabs.push({name:"New Tab",categories:[],immutable:!1,active:!1}),this.switchtab(this.tabs[this.tabs.length-1])},renametab:function(){if(!this.active_tab.immutable){var e=this.active_tab,t=window.prompt("Type the desired tab name:",e.name);null!==t&&""!==t&&null!==e&&(e.name=t)}},deltab:function(e){e||(e=this.active_tab),e.immutable||(this.switchtab(this.tabs[0]),this.tabs.splice(this.tabs.indexOf(e),1))},movetab:function(e,t){if(e&&!e.immutable){var s=this.tabs.indexOf(e),a=s+t;this.tabs.splice(a,0,this.tabs.splice(s,1)[0])}},tab_unread_count:function(e){var t=0,s=this.unread_messages;return e.categories.find((function(e){s[e]&&(t+=s[e])})),t},tab_unread_categories:function(e){var t=!1,s=this.unread_messages;return e.categories.find((function(e){if(s[e])return t=!0,!0})),{red:t,grey:!t}},attempt_archive:function(){if(this.messages.length>this.showingnum){var e=this.messages.splice(0,20);Array.prototype.push.apply(this.archived_messages,e)}},apply_filter:function(e){this.shown_messages.splice(0),this.unshown_messages=0,this.messages.forEach((function(t){e.indexOf(t.category)>-1&&this.shown_messages.push(t)}),this),this.archived_messages.forEach((function(t){e.indexOf(t.category)>-1&&this.unshown_messages++}),this)},add_message:function(e){let t={time:e.time,category:"error",content:e.message,repeats:1};if(t.category=this.get_category(t.content),this.crushing){let e=this.messages.slice(-this.crushing);for(let s=e.length-1;s>=0;s--){let a=e[s];a.content==t.content&&(t.repeats+=a.repeats,this.messages.splice(this.messages.indexOf(a),1))}}t.content=t.content.replace(/(\b(https?):\/\/[\-A-Z0-9+&@#\/%?=~_|!:,.;]*[\-A-Z0-9+&@#\/%=~_|])/gim,'$1'),this.current_categories.length&&this.current_categories.indexOf(t.category)<0?(isNaN(this.unread_messages[t.category])&&(this.unread_messages[t.category]=0),this.unread_messages[t.category]+=1):this.current_categories.length&&this.shown_messages.push(t),t.id=++vchat_state.lastId,this.attempt_archive(),this.messages.push(t)},internal_message:function(e){let t={time:this.messages.length?this.messages.slice(-1).time+1:0,category:"vc_system",content:"[VChat Internal] "+e+""};t.id=++vchat_state.lastId,this.messages.push(t)},on_mouseup:function(e){let t=e.target;"getSelection"in window&&!1===window.getSelection().isCollapsed||t&&("INPUT"===t.tagName||"TEXTAREA"===t.tagName)||(focusMapWindow(),e.preventDefault(),e.target.click())},click_message:function(e){let t=e.target;if("A"===t.tagName){e.stopPropagation(),e.preventDefault?e.preventDefault():e.returnValue=!1;var s=t.getAttribute("href");"?"==s[0]||s.length>=8&&"byond://"==s.substring(0,8)?window.location=s:window.location="byond://?action=openLink&link="+encodeURIComponent(s)}},get_category:function(e){if(!vchat_state.ready)return void push_Topic("not_ready");let t=domparser.parseFromString(e,"text/html").querySelector("span"),s="nomatch";return t?(this.type_table.find((function(e){if(t.msMatchesSelector(e.matches))return s=e.becomes,!0})),s):s},save_chatlog:function(){var e="";this.archived_messages.concat(this.messages).forEach((function(t){e+=t.content,t.repeats>1&&(e+="(x"+t.repeats+")"),e+="
\n"})),e+="";var t=new Date,s=String(t.getHours());s.length<2&&(s="0"+s);var a=String(t.getMinutes());a.length<2&&(a="0"+a);var n=String(t.getDate());n.length<2&&(n="0"+n);var o=String(t.getMonth()+1);o.length<2&&(o="0"+o);var i="log"+(" "+String(t.getFullYear())+"-"+o+"-"+n+" ("+s+" "+a+")")+".html",r=document.createElement("a");if(void 0!==r.download)r.href="data:attachment/text,"+encodeURI(e),r.target="_blank",r.download=i,r.click();else{var c=new Blob([e],{type:"text/html;charset=utf8;"});saved=window.navigator.msSaveOrOpenBlob(c,i)}},do_latency_test:function(){send_latency_check()},blur_this:function(e){e.target.blur()}}})}function check_ping(){Date.now()-vchat_state.lastPingReceived>vchat_opts.msBeforeDropped&&(vueapp.reconnecting=!0)}function send_latency_check(){vchat_state.latency_sent||(vchat_state.latency_sent=Date.now(),vueapp.latency="?",push_Topic("ping"),setTimeout((function(){"?"==vchat_state.latency_ms&&(vchat_state.latency_ms=999)}),1e3),setTimeout((function(){vchat_state.latency_sent=0,vueapp.latency=0}),5e3))}function get_latency_check(){vchat_state.latency_sent&&(vueapp.latency=Date.now()-vchat_state.latency_sent)}function byondDecode(e){e=e.replace(/\+/g,"%20");try{e=decodeURIComponent(e)}catch(t){e=unescape(e)}return JSON.parse(e)}function putmessage(e){e=byondDecode(e),Array.isArray(e)?e.forEach((function(e){vueapp.add_message(e)})):"object"==typeof e&&vueapp.add_message(e)}function system_message(e){vueapp.internal_message(e)}function push_Topic(e){window.location="?_src_=chat&proc="+e}function push_Topic_showingnum(e){window.location="?_src_=chat&showingnum="+e}function focusMapWindow(){window.location="byond://winset?mapwindow.map.focus=true"}function send_debug(e){push_Topic("debug¶m[message]="+encodeURIComponent(e))}function get_event(e){if(vchat_state.ready){var t;switch((t=byondDecode(e)).evttype){case"internal_error":system_message("Event parse error: "+e);break;case"byond_player":send_client_data(),vueapp.is_admin="true"===t.admin,vchat_state.byond_ip=t.address,vchat_state.byond_cid=t.cid,vchat_state.byond_ckey=t.ckey,set_storage("ip",vchat_state.byond_ip),set_storage("cid",vchat_state.byond_cid),set_storage("ckey",vchat_state.byond_ckey);break;case"keepalive":vchat_state.lastPingReceived=Date.now(),vueapp.reconnecting=!1;break;case"pong":get_latency_check();break;case"availability":push_Topic("done_loading");break;default:system_message("Didn't know what to do with event: "+e)}}else push_Topic("not_ready")}function send_client_data(){let e={ip:get_storage("ip"),cid:get_storage("cid"),ckey:get_storage("ckey")};push_Topic("ident¶m[clientdata]="+JSON.stringify(e))}function set_localstorage(e,t){window.localStorage.setItem(vchat_opts.cookiePrefix+e,t)}function get_localstorage(e,t){let s=window.localStorage.getItem(vchat_opts.cookiePrefix+e);return"null"===s||null===s?s=t:"true"===s?s=!0:"false"===s?s=!1:isNaN(s)||(s=+s),s}function set_cookie(e,t){let s=new Date;s.setFullYear(s.getFullYear()+1);let a=s.toUTCString();document.cookie=vchat_opts.cookiePrefix+e+"="+t+";expires="+a+";path=/"}function get_cookie(e,t){let s=document.cookie.split(";"),a={};s.forEach((function(e){let s=e.replace(vchat_opts.cookiePrefix,"").trim(),n=s.search("="),o=decodeURIComponent(s.substring(0,n)),i=decodeURIComponent(s.substring(n+1));"null"==i||null===i?i=t:"true"===i?i=!0:"false"===i?i=!1:isNaN(i)||(i=+i),a[o]=i})),a[e]}var SKIN_BUTTONS=["rpane.textb","rpane.infob","rpane.wikib","rpane.forumb","rpane.rulesb","rpane.github","rpane.discord","rpane.mapb","rpane.changelog","mainwindow.saybutton","mainwindow.mebutton","mainwindow.hotkey_toggle"],SKIN_ELEMENTS=["mainwindow","mainwindow.mainvsplit","mainwindow.tooltip","rpane","rpane.rpanewindow","rpane.mediapanel"];function switch_ui_mode(e){doWinset(SKIN_BUTTONS.reduce((function(t,s){return t[s+".background-color"]=e.buttonBgColor,t}),{})),doWinset(SKIN_BUTTONS.reduce((function(t,s){return t[s+".text-color"]=e.buttonTextColor,t}),{})),doWinset(SKIN_ELEMENTS.reduce((function(t,s){return t[s+".background-color"]=e.windowBgColor,t}),{})),doWinset("infowindow",{"background-color":e.tabBackgroundColor,"text-color":e.tabTextColor}),doWinset("infowindow.info",{"background-color":e.tabBackgroundColor,"text-color":e.tabTextColor,"highlight-color":e.highlightColor,"tab-text-color":e.tabTextColor,"tab-background-color":e.tabBackgroundColor})}function doWinset(e,t){void 0===t&&(t=e,e=null);var s="byond://winset?";e&&(s+="id="+e+"&"),s+=Object.keys(t).map((function(e){return e+"="+encodeURIComponent(t[e])})).join("&"),window.location=s} diff --git a/code/modules/vchat/js/vue.min.js b/code/modules/vchat/js/vue.min.js index e22cf13003..d884a27e32 100644 --- a/code/modules/vchat/js/vue.min.js +++ b/code/modules/vchat/js/vue.min.js @@ -1,11965 +1,11 @@ /*! - * Vue.js v2.6.11 - * (c) 2014-2019 Evan You + * Vue.js v2.7.10 + * (c) 2014-2022 Evan You * Released under the MIT License. */ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global = global || self, global.Vue = factory()); -}(this, function () { 'use strict'; - - /* */ - - var emptyObject = Object.freeze({}); - - // These helpers produce better VM code in JS engines due to their - // explicitness and function inlining. - function isUndef (v) { - return v === undefined || v === null - } - - function isDef (v) { - return v !== undefined && v !== null - } - - function isTrue (v) { - return v === true - } - - function isFalse (v) { - return v === false - } - - /** - * Check if value is primitive. - */ - function isPrimitive (value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - // $flow-disable-line - typeof value === 'symbol' || - typeof value === 'boolean' - ) - } - - /** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ - function isObject (obj) { - return obj !== null && typeof obj === 'object' - } - - /** - * Get the raw type string of a value, e.g., [object Object]. - */ - var _toString = Object.prototype.toString; - - function toRawType (value) { - return _toString.call(value).slice(8, -1) - } - - /** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ - function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' - } - - function isRegExp (v) { - return _toString.call(v) === '[object RegExp]' - } - - /** - * Check if val is a valid array index. - */ - function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) - } - - function isPromise (val) { - return ( - isDef(val) && - typeof val.then === 'function' && - typeof val.catch === 'function' - ) - } - - /** - * Convert a value to a string that is actually rendered. - */ - function toString (val) { - return val == null - ? '' - : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString) - ? JSON.stringify(val, null, 2) - : String(val) - } - - /** - * Convert an input value to a number for persistence. - * If the conversion fails, return original string. - */ - function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n - } - - /** - * Make a map and return a function for checking if a key - * is in that map. - */ - function makeMap ( - str, - expectsLowerCase - ) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } - } - - /** - * Check if a tag is a built-in tag. - */ - var isBuiltInTag = makeMap('slot,component', true); - - /** - * Check if an attribute is a reserved attribute. - */ - var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - - /** - * Remove an item from an array. - */ - function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } - } - - /** - * Check whether an object has the property. - */ - var hasOwnProperty = Object.prototype.hasOwnProperty; - function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) - } - - /** - * Create a cached version of a pure function. - */ - function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) - } - - /** - * Camelize a hyphen-delimited string. - */ - var camelizeRE = /-(\w)/g; - var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) - }); - - /** - * Capitalize a string. - */ - var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) - }); - - /** - * Hyphenate a camelCase string. - */ - var hyphenateRE = /\B([A-Z])/g; - var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() - }); - - /** - * Simple bind polyfill for environments that do not support it, - * e.g., PhantomJS 1.x. Technically, we don't need this anymore - * since native bind is now performant enough in most browsers. - * But removing it would mean breaking code that was able to run in - * PhantomJS 1.x, so this must be kept for backward compatibility. - */ - - /* istanbul ignore next */ - function polyfillBind (fn, ctx) { - function boundFn (a) { - var l = arguments.length; - return l - ? l > 1 - ? fn.apply(ctx, arguments) - : fn.call(ctx, a) - : fn.call(ctx) - } - - boundFn._length = fn.length; - return boundFn - } - - function nativeBind (fn, ctx) { - return fn.bind(ctx) - } - - var bind = Function.prototype.bind - ? nativeBind - : polyfillBind; - - /** - * Convert an Array-like object to a real Array. - */ - function toArray (list, start) { - start = start || 0; - var i = list.length - start; - var ret = new Array(i); - while (i--) { - ret[i] = list[i + start]; - } - return ret - } - - /** - * Mix properties into target object. - */ - function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to - } - - /** - * Merge an Array of Objects into a single Object. - */ - function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res - } - - /* eslint-disable no-unused-vars */ - - /** - * Perform no operation. - * Stubbing args to make Flow happy without leaving useless transpiled code - * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/). - */ - function noop (a, b, c) {} - - /** - * Always return false. - */ - var no = function (a, b, c) { return false; }; - - /* eslint-enable no-unused-vars */ - - /** - * Return the same value. - */ - var identity = function (_) { return _; }; - - /** - * Generate a string containing static keys from compiler modules. - */ - function genStaticKeys (modules) { - return modules.reduce(function (keys, m) { - return keys.concat(m.staticKeys || []) - }, []).join(',') - } - - /** - * Check if two values are loosely equal - that is, - * if they are plain objects, do they have the same shape? - */ - function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (a instanceof Date && b instanceof Date) { - return a.getTime() === b.getTime() - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } - } - - /** - * Return the first index at which a loosely equal value can be - * found in the array (if value is a plain object, the array must - * contain an object of the same shape), or -1 if it is not present. - */ - function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 - } - - /** - * Ensure a function is called only once. - */ - function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } - } - - var SSR_ATTR = 'data-server-rendered'; - - var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' - ]; - - var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured', - 'serverPrefetch' - ]; - - /* */ - - - - var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - // $flow-disable-line - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: "development" !== 'production', - - /** - * Whether to enable devtools - */ - devtools: "development" !== 'production', - - /** - * Whether to record perf - */ - performance: false, - - /** - * Error handler for watcher errors - */ - errorHandler: null, - - /** - * Warn handler for watcher warns - */ - warnHandler: null, - - /** - * Ignore certain custom elements - */ - ignoredElements: [], - - /** - * Custom user key aliases for v-on - */ - // $flow-disable-line - keyCodes: Object.create(null), - - /** - * Check if a tag is reserved so that it cannot be registered as a - * component. This is platform-dependent and may be overwritten. - */ - isReservedTag: no, - - /** - * Check if an attribute is reserved so that it cannot be used as a component - * prop. This is platform-dependent and may be overwritten. - */ - isReservedAttr: no, - - /** - * Check if a tag is an unknown element. - * Platform-dependent. - */ - isUnknownElement: no, - - /** - * Get the namespace of an element - */ - getTagNamespace: noop, - - /** - * Parse the real tag name for the specific platform. - */ - parsePlatformTagName: identity, - - /** - * Check if an attribute must be bound using property, e.g. value - * Platform-dependent. - */ - mustUseProp: no, - - /** - * Perform updates asynchronously. Intended to be used by Vue Test Utils - * This will significantly reduce performance if set to false. - */ - async: true, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS - }); - - /* */ - - /** - * unicode letters used for parsing html tags, component names and property paths. - * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname - * skipping \u10000-\uEFFFF due to it freezing up PhantomJS - */ - var unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/; - - /** - * Check if a string starts with $ or _ - */ - function isReserved (str) { - var c = (str + '').charCodeAt(0); - return c === 0x24 || c === 0x5F - } - - /** - * Define a property. - */ - function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); - } - - /** - * Parse simple path. - */ - var bailRE = new RegExp(("[^" + (unicodeRegExp.source) + ".$_\\d]")); - function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } - } - - /* */ - - // can we use __proto__? - var hasProto = '__proto__' in {}; - - // Browser environment sniffing - var inBrowser = typeof window !== 'undefined'; - var inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform; - var weexPlatform = inWeex && WXEnvironment.platform.toLowerCase(); - var UA = inBrowser && window.navigator.userAgent.toLowerCase(); - var isIE = UA && /msie|trident/.test(UA); - var isIE9 = UA && UA.indexOf('msie 9.0') > 0; - var isEdge = UA && UA.indexOf('edge/') > 0; - var isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android'); - var isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios'); - var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - var isPhantomJS = UA && /phantomjs/.test(UA); - var isFF = UA && UA.match(/firefox\/(\d+)/); - - // Firefox has a "watch" function on Object.prototype... - var nativeWatch = ({}).watch; - - var supportsPassive = false; - if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - supportsPassive = true; - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} - } - - // this needs to be lazy-evaled because vue may be required before - // vue-server-renderer can set VUE_ENV - var _isServer; - var isServerRendering = function () { - if (_isServer === undefined) { - /* istanbul ignore if */ - if (!inBrowser && !inWeex && typeof global !== 'undefined') { - // detect presence of vue-server-renderer and avoid - // Webpack shimming the process - _isServer = global['process'] && global['process'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer - }; - - // detect devtools - var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - - /* istanbul ignore next */ - function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) - } - - var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - - var _Set; - /* istanbul ignore if */ // $flow-disable-line - if (typeof Set !== 'undefined' && isNative(Set)) { - // use native Set when available. - _Set = Set; - } else { - // a non-standard Set polyfill that only works with primitive keys. - _Set = /*@__PURE__*/(function () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); - } - - /* */ - - var warn = noop; - var tip = noop; - var generateComponentTrace = (noop); // work around flow check - var formatComponentName = (noop); - - { - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var trace = vm ? generateComponentTrace(vm) : ''; - - if (config.warnHandler) { - config.warnHandler.call(null, msg, vm, trace); - } else if (hasConsole && (!config.silent)) { - console.error(("[Vue warn]: " + msg + trace)); - } - }; - - tip = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var last = tree[tree.length - 1]; - if (last.constructor === vm.constructor) { - currentRecursiveSequence++; - vm = vm.$parent; - continue - } else if (currentRecursiveSequence > 0) { - tree[tree.length - 1] = [last, currentRecursiveSequence]; - currentRecursiveSequence = 0; - } - } - tree.push(vm); - vm = vm.$parent; - } - return '\n\nfound in\n\n' + tree - .map(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; - } - - /* */ - - var uid = 0; - - /** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ - var Dep = function Dep () { - this.id = uid++; - this.subs = []; - }; - - Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); - }; - - Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); - }; - - Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } - }; - - Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - if (!config.async) { - // subs aren't sorted in scheduler if not running async - // we need to sort them now to make sure they fire in correct - // order - subs.sort(function (a, b) { return a.id - b.id; }); - } - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } - }; - - // The current target watcher being evaluated. - // This is globally unique because only one watcher - // can be evaluated at a time. - Dep.target = null; - var targetStack = []; - - function pushTarget (target) { - targetStack.push(target); - Dep.target = target; - } - - function popTarget () { - targetStack.pop(); - Dep.target = targetStack[targetStack.length - 1]; - } - - /* */ - - var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory - ) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.fnContext = undefined; - this.fnOptions = undefined; - this.fnScopeId = undefined; - this.key = data && data.key; - this.componentOptions = componentOptions; - this.componentInstance = undefined; - this.parent = undefined; - this.raw = false; - this.isStatic = false; - this.isRootInsert = true; - this.isComment = false; - this.isCloned = false; - this.isOnce = false; - this.asyncFactory = asyncFactory; - this.asyncMeta = undefined; - this.isAsyncPlaceholder = false; - }; - - var prototypeAccessors = { child: { configurable: true } }; - - // DEPRECATED: alias for componentInstance for backwards compat. - /* istanbul ignore next */ - prototypeAccessors.child.get = function () { - return this.componentInstance - }; - - Object.defineProperties( VNode.prototype, prototypeAccessors ); - - var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node - }; - - function createTextVNode (val) { - return new VNode(undefined, undefined, undefined, String(val)) - } - - // optimized shallow clone - // used for static nodes and slot nodes because they may be reused across - // multiple renders, cloning them avoids errors when DOM manipulations rely - // on their elm reference. - function cloneVNode (vnode) { - var cloned = new VNode( - vnode.tag, - vnode.data, - // #7975 - // clone children array to avoid mutating original in case of cloning - // a child. - vnode.children && vnode.children.slice(), - vnode.text, - vnode.elm, - vnode.context, - vnode.componentOptions, - vnode.asyncFactory - ); - cloned.ns = vnode.ns; - cloned.isStatic = vnode.isStatic; - cloned.key = vnode.key; - cloned.isComment = vnode.isComment; - cloned.fnContext = vnode.fnContext; - cloned.fnOptions = vnode.fnOptions; - cloned.fnScopeId = vnode.fnScopeId; - cloned.asyncMeta = vnode.asyncMeta; - cloned.isCloned = true; - return cloned - } - - /* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - - var arrayProto = Array.prototype; - var arrayMethods = Object.create(arrayProto); - - var methodsToPatch = [ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' - ]; - - /** - * Intercept mutating methods and emit events - */ - methodsToPatch.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); - }); - - /* */ - - var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - - /** - * In some cases we may want to disable observation inside a component's - * update computation. - */ - var shouldObserve = true; - - function toggleObserving (value) { - shouldObserve = value; - } - - /** - * Observer class that is attached to each observed - * object. Once attached, the observer converts the target - * object's property keys into getter/setters that - * collect dependencies and dispatch updates. - */ - var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - if (hasProto) { - protoAugment(value, arrayMethods); - } else { - copyAugment(value, arrayMethods, arrayKeys); - } - this.observeArray(value); - } else { - this.walk(value); - } - }; - - /** - * Walk through all properties and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ - Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive$$1(obj, keys[i]); - } - }; - - /** - * Observe a list of Array items. - */ - Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } - }; - - // helpers - - /** - * Augment a target Object or Array by intercepting - * the prototype chain using __proto__ - */ - function protoAugment (target, src) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ - } - - /** - * Augment a target Object or Array by defining - * hidden properties. - */ - /* istanbul ignore next */ - function copyAugment (target, src, keys) { - for (var i = 0, l = keys.length; i < l; i++) { - var key = keys[i]; - def(target, key, src[key]); - } - } - - /** - * Attempt to create an observer instance for a value, - * returns the new observer if successfully observed, - * or the existing observer if the value already has one. - */ - function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - shouldObserve && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob - } - - /** - * Define a reactive property on an Object. - */ - function defineReactive$$1 ( - obj, - key, - val, - customSetter, - shallow - ) { - var dep = new Dep(); - - var property = Object.getOwnPropertyDescriptor(obj, key); - if (property && property.configurable === false) { - return - } - - // cater for pre-defined getter/setters - var getter = property && property.get; - var setter = property && property.set; - if ((!getter || setter) && arguments.length === 2) { - val = obj[key]; - } - - var childOb = !shallow && observe(val); - Object.defineProperty(obj, key, { - enumerable: true, - configurable: true, - get: function reactiveGetter () { - var value = getter ? getter.call(obj) : val; - if (Dep.target) { - dep.depend(); - if (childOb) { - childOb.dep.depend(); - if (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if (customSetter) { - customSetter(); - } - // #7981: for accessor properties without setter - if (getter && !setter) { return } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); - } - - /** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ - function set (target, key, val) { - if (isUndef(target) || isPrimitive(target) - ) { - warn(("Cannot set reactive property on undefined, null, or primitive value: " + ((target)))); - } - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - warn( - 'Avoid adding reactive properties to a Vue instance or its root $data ' + - 'at runtime - declare it upfront in the data option.' - ); - return val - } - if (!ob) { - target[key] = val; - return val - } - defineReactive$$1(ob.value, key, val); - ob.dep.notify(); - return val - } - - /** - * Delete a property and trigger change if necessary. - */ - function del (target, key) { - if (isUndef(target) || isPrimitive(target) - ) { - warn(("Cannot delete reactive property on undefined, null, or primitive value: " + ((target)))); - } - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.splice(key, 1); - return - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - warn( - 'Avoid deleting properties on a Vue instance or its root $data ' + - '- just set it to null.' - ); - return - } - if (!hasOwn(target, key)) { - return - } - delete target[key]; - if (!ob) { - return - } - ob.dep.notify(); - } - - /** - * Collect dependencies on array elements when the array is touched, since - * we cannot intercept array element access like property getters. - */ - function dependArray (value) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } - } - - /* */ - - /** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ - var strats = config.optionMergeStrategies; - - /** - * Options with restrictions - */ - { - strats.el = strats.propsData = function (parent, child, vm, key) { - if (!vm) { - warn( - "option \"" + key + "\" can only be used during instance " + - 'creation with the `new` keyword.' - ); - } - return defaultStrat(parent, child) - }; - } - - /** - * Helper that recursively merges two data objects together. - */ - function mergeData (to, from) { - if (!from) { return to } - var key, toVal, fromVal; - - var keys = hasSymbol - ? Reflect.ownKeys(from) - : Object.keys(from); - - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - // in case the object is already observed... - if (key === '__ob__') { continue } - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if ( - toVal !== fromVal && - isPlainObject(toVal) && - isPlainObject(fromVal) - ) { - mergeData(toVal, fromVal); - } - } - return to - } - - /** - * Data - */ - function mergeDataOrFn ( - parentVal, - childVal, - vm - ) { - if (!vm) { - // in a Vue.extend merge, both should be functions - if (!childVal) { - return parentVal - } - if (!parentVal) { - return childVal - } - // when parentVal & childVal are both present, - // we need to return a function that returns the - // merged result of both functions... no need to - // check if parentVal is a function here because - // it has to be a function to pass previous merges. - return function mergedDataFn () { - return mergeData( - typeof childVal === 'function' ? childVal.call(this, this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm, vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm, vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } - } - - strats.data = function ( - parentVal, - childVal, - vm - ) { - if (!vm) { - if (childVal && typeof childVal !== 'function') { - warn( - 'The "data" option should be a function ' + - 'that returns a per-instance value in component ' + - 'definitions.', - vm - ); - - return parentVal - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) - }; - - /** - * Hooks and props are merged as arrays. - */ - function mergeHook ( - parentVal, - childVal - ) { - var res = childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal; - return res - ? dedupeHooks(res) - : res - } - - function dedupeHooks (hooks) { - var res = []; - for (var i = 0; i < hooks.length; i++) { - if (res.indexOf(hooks[i]) === -1) { - res.push(hooks[i]); - } - } - return res - } - - LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; - }); - - /** - * Assets - * - * When a vm is present (instance creation), we need to do - * a three-way merge between constructor options, instance - * options and parent options. - */ - function mergeAssets ( - parentVal, - childVal, - vm, - key - ) { - var res = Object.create(parentVal || null); - if (childVal) { - assertObjectType(key, childVal, vm); - return extend(res, childVal) - } else { - return res - } - } - - ASSET_TYPES.forEach(function (type) { - strats[type + 's'] = mergeAssets; - }); - - /** - * Watchers. - * - * Watchers hashes should not overwrite one - * another, so we merge them as arrays. - */ - strats.watch = function ( - parentVal, - childVal, - vm, - key - ) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret - }; - - /** - * Other object hashes. - */ - strats.props = - strats.methods = - strats.inject = - strats.computed = function ( - parentVal, - childVal, - vm, - key - ) { - if (childVal && "development" !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret - }; - strats.provide = mergeDataOrFn; - - /** - * Default strategy. - */ - var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal - }; - - /** - * Validate component names - */ - function checkComponents (options) { - for (var key in options.components) { - validateComponentName(key); - } - } - - function validateComponentName (name) { - if (!new RegExp(("^[a-zA-Z][\\-\\.0-9_" + (unicodeRegExp.source) + "]*$")).test(name)) { - warn( - 'Invalid component name: "' + name + '". Component names ' + - 'should conform to valid custom element name in html5 specification.' - ); - } - if (isBuiltInTag(name) || config.isReservedTag(name)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + name - ); - } - } - - /** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ - function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else { - warn( - "Invalid value for option \"props\": expected an Array or an Object, " + - "but got " + (toRawType(props)) + ".", - vm - ); - } - options.props = res; - } - - /** - * Normalize all injections into Object-based format - */ - function normalizeInject (options, vm) { - var inject = options.inject; - if (!inject) { return } - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else { - warn( - "Invalid value for option \"inject\": expected an Array or an Object, " + - "but got " + (toRawType(inject)) + ".", - vm - ); - } - } - - /** - * Normalize raw function directives into object format. - */ - function normalizeDirectives (options) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def$$1 = dirs[key]; - if (typeof def$$1 === 'function') { - dirs[key] = { bind: def$$1, update: def$$1 }; - } - } - } - } - - function assertObjectType (name, value, vm) { - if (!isPlainObject(value)) { - warn( - "Invalid value for option \"" + name + "\": expected an Object, " + - "but got " + (toRawType(value)) + ".", - vm - ); - } - } - - /** - * Merge two option objects into a new one. - * Core utility used in both instantiation and inheritance. - */ - function mergeOptions ( - parent, - child, - vm - ) { - { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - - // Apply extends and mixins on the child options, - // but only if it is a raw options object that isn't - // the result of another mergeOptions call. - // Only merged options has the _base property. - if (!child._base) { - if (child.extends) { - parent = mergeOptions(parent, child.extends, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - } - - var options = {}; - var key; - for (key in parent) { - mergeField(key); - } - for (key in child) { - if (!hasOwn(parent, key)) { - mergeField(key); - } - } - function mergeField (key) { - var strat = strats[key] || defaultStrat; - options[key] = strat(parent[key], child[key], vm, key); - } - return options - } - - /** - * Resolve an asset. - * This function is used because child instances need access - * to assets defined in its ancestor chain. - */ - function resolveAsset ( - options, - type, - id, - warnMissing - ) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if (warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res - } - - /* */ - - - - function validateProp ( - key, - propOptions, - propsData, - vm - ) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // boolean casting - var booleanIndex = getTypeIndex(Boolean, prop.type); - if (booleanIndex > -1) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (value === '' || value === hyphenate(key)) { - // only cast empty string / same name to boolean if - // boolean has higher priority - var stringIndex = getTypeIndex(String, prop.type); - if (stringIndex < 0 || booleanIndex < stringIndex) { - value = true; - } - } - } - // check default value - if (value === undefined) { - value = getPropDefaultValue(vm, prop, key); - // since the default value is a fresh copy, - // make sure to observe it. - var prevShouldObserve = shouldObserve; - toggleObserving(true); - observe(value); - toggleObserving(prevShouldObserve); - } - { - assertProp(prop, key, value, vm, absent); - } - return value - } - - /** - * Get the default value of a prop. - */ - function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if (isObject(def)) { - warn( - 'Invalid default value for prop "' + key + '": ' + - 'Props with type Object/Array must use a factory function ' + - 'to return the default value.', - vm - ); - } - // the raw prop value was also undefined from previous render, - // return previous default value to avoid unnecessary watcher trigger - if (vm && vm.$options.propsData && - vm.$options.propsData[key] === undefined && - vm._props[key] !== undefined - ) { - return vm._props[key] - } - // call factory function for non-Function types - // a value is Function if its prototype is function even across different execution context - return typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def - } - - /** - * Assert whether a prop is valid. - */ - function assertProp ( - prop, - name, - value, - vm, - absent - ) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.isArray(type)) { - type = [type]; - } - for (var i = 0; i < type.length && !valid; i++) { - var assertedType = assertType(value, type[i]); - expectedTypes.push(assertedType.expectedType || ''); - valid = assertedType.valid; - } - } - - if (!valid) { - warn( - getInvalidTypeMessage(name, value, expectedTypes), - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } - } - - var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - - function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var t = typeof value; - valid = t === expectedType.toLowerCase(); - // for primitive wrapper objects - if (!valid && t === 'object') { - valid = value instanceof type; - } - } else if (expectedType === 'Object') { - valid = isPlainObject(value); - } else if (expectedType === 'Array') { - valid = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } - } - - /** - * Use function string name to check built-in types, - * because a simple equality check will fail when running - * across different vms / iframes. - */ - function getType (fn) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' - } - - function isSameType (a, b) { - return getType(a) === getType(b) - } - - function getTypeIndex (type, expectedTypes) { - if (!Array.isArray(expectedTypes)) { - return isSameType(expectedTypes, type) ? 0 : -1 - } - for (var i = 0, len = expectedTypes.length; i < len; i++) { - if (isSameType(expectedTypes[i], type)) { - return i - } - } - return -1 - } - - function getInvalidTypeMessage (name, value, expectedTypes) { - var message = "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')); - var expectedType = expectedTypes[0]; - var receivedType = toRawType(value); - var expectedValue = styleValue(value, expectedType); - var receivedValue = styleValue(value, receivedType); - // check if we need to specify expected value - if (expectedTypes.length === 1 && - isExplicable(expectedType) && - !isBoolean(expectedType, receivedType)) { - message += " with value " + expectedValue; - } - message += ", got " + receivedType + " "; - // check if we need to specify received value - if (isExplicable(receivedType)) { - message += "with value " + receivedValue + "."; - } - return message - } - - function styleValue (value, type) { - if (type === 'String') { - return ("\"" + value + "\"") - } else if (type === 'Number') { - return ("" + (Number(value))) - } else { - return ("" + value) - } - } - - function isExplicable (value) { - var explicitTypes = ['string', 'number', 'boolean']; - return explicitTypes.some(function (elem) { return value.toLowerCase() === elem; }) - } - - function isBoolean () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - return args.some(function (elem) { return elem.toLowerCase() === 'boolean'; }) - } - - /* */ - - function handleError (err, vm, info) { - // Deactivate deps tracking while processing error handler to avoid possible infinite rendering. - // See: https://github.com/vuejs/vuex/issues/1505 - pushTarget(); - try { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); - } finally { - popTarget(); - } - } - - function invokeWithErrorHandling ( - handler, - context, - args, - vm, - info - ) { - var res; - try { - res = args ? handler.apply(context, args) : handler.call(context); - if (res && !res._isVue && isPromise(res) && !res._handled) { - res.catch(function (e) { return handleError(e, vm, info + " (Promise/async)"); }); - // issue #9511 - // avoid catch triggering multiple times when nested calls - res._handled = true; - } - } catch (e) { - handleError(e, vm, info); - } - return res - } - - function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - // if the user intentionally throws the original error in the handler, - // do not log it twice - if (e !== err) { - logError(e, null, 'config.errorHandler'); - } - } - } - logError(err, vm, info); - } - - function logError (err, vm, info) { - { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if ((inBrowser || inWeex) && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } - } - - /* */ - - var isUsingMicroTask = false; - - var callbacks = []; - var pending = false; - - function flushCallbacks () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } - } - - // Here we have async deferring wrappers using microtasks. - // In 2.5 we used (macro) tasks (in combination with microtasks). - // However, it has subtle problems when state is changed right before repaint - // (e.g. #6813, out-in transitions). - // Also, using (macro) tasks in event handler would cause some weird behaviors - // that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109). - // So we now use microtasks everywhere, again. - // A major drawback of this tradeoff is that there are some scenarios - // where microtasks have too high a priority and fire in between supposedly - // sequential events (e.g. #4521, #6690, which have workarounds) - // or even between bubbling of the same event (#6566). - var timerFunc; - - // The nextTick behavior leverages the microtask queue, which can be accessed - // via either native Promise.then or MutationObserver. - // MutationObserver has wider support, however it is seriously bugged in - // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It - // completely stops working after triggering a few times... so, if native - // Promise is available, we will use it: - /* istanbul ignore next, $flow-disable-line */ - if (typeof Promise !== 'undefined' && isNative(Promise)) { - var p = Promise.resolve(); - timerFunc = function () { - p.then(flushCallbacks); - // In problematic UIWebViews, Promise.then doesn't completely break, but - // it can get stuck in a weird state where callbacks are pushed into the - // microtask queue but the queue isn't being flushed, until the browser - // needs to do some other work, e.g. handle a timer. Therefore we can - // "force" the microtask queue to be flushed by adding an empty timer. - if (isIOS) { setTimeout(noop); } - }; - isUsingMicroTask = true; - } else if (!isIE && typeof MutationObserver !== 'undefined' && ( - isNative(MutationObserver) || - // PhantomJS and iOS 7.x - MutationObserver.toString() === '[object MutationObserverConstructor]' - )) { - // Use MutationObserver where native Promise is not available, - // e.g. PhantomJS, iOS7, Android 4.4 - // (#6466 MutationObserver is unreliable in IE11) - var counter = 1; - var observer = new MutationObserver(flushCallbacks); - var textNode = document.createTextNode(String(counter)); - observer.observe(textNode, { - characterData: true - }); - timerFunc = function () { - counter = (counter + 1) % 2; - textNode.data = String(counter); - }; - isUsingMicroTask = true; - } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - // Fallback to setImmediate. - // Technically it leverages the (macro) task queue, - // but it is still a better choice than setTimeout. - timerFunc = function () { - setImmediate(flushCallbacks); - }; - } else { - // Fallback to setTimeout. - timerFunc = function () { - setTimeout(flushCallbacks, 0); - }; - } - - function nextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - handleError(e, ctx, 'nextTick'); - } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - timerFunc(); - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve) { - _resolve = resolve; - }) - } - } - - /* */ - - var mark; - var measure; - - { - var perf = inBrowser && window.performance; - /* istanbul ignore if */ - if ( - perf && - perf.mark && - perf.measure && - perf.clearMarks && - perf.clearMeasures - ) { - mark = function (tag) { return perf.mark(tag); }; - measure = function (name, startTag, endTag) { - perf.measure(name, startTag, endTag); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - // perf.clearMeasures(name) - }; - } - } - - /* not type checking this file because flow doesn't play well with Proxy */ - - var initProxy; - - { - var allowedGlobals = makeMap( - 'Infinity,undefined,NaN,isFinite,isNaN,' + - 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + - 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + - 'require' // for Webpack/Browserify - ); - - var warnNonPresent = function (target, key) { - warn( - "Property or method \"" + key + "\" is not defined on the instance but " + - 'referenced during render. Make sure that this property is reactive, ' + - 'either in the data option, or for class-based components, by ' + - 'initializing the property. ' + - 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', - target - ); - }; - - var warnReservedPrefix = function (target, key) { - warn( - "Property \"" + key + "\" must be accessed with \"$data." + key + "\" because " + - 'properties starting with "$" or "_" are not proxied in the Vue instance to ' + - 'prevent conflicts with Vue internals. ' + - 'See: https://vuejs.org/v2/api/#data', - target - ); - }; - - var hasProxy = - typeof Proxy !== 'undefined' && isNative(Proxy); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - var hasHandler = { - has: function has (target, key) { - var has = key in target; - var isAllowed = allowedGlobals(key) || - (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)); - if (!has && !isAllowed) { - if (key in target.$data) { warnReservedPrefix(target, key); } - else { warnNonPresent(target, key); } - } - return has || !isAllowed - } - }; - - var getHandler = { - get: function get (target, key) { - if (typeof key === 'string' && !(key in target)) { - if (key in target.$data) { warnReservedPrefix(target, key); } - else { warnNonPresent(target, key); } - } - return target[key] - } - }; - - initProxy = function initProxy (vm) { - if (hasProxy) { - // determine which proxy handler to use - var options = vm.$options; - var handlers = options.render && options.render._withStripped - ? getHandler - : hasHandler; - vm._renderProxy = new Proxy(vm, handlers); - } else { - vm._renderProxy = vm; - } - }; - } - - /* */ - - var seenObjects = new _Set(); - - /** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ - function traverse (val) { - _traverse(val, seenObjects); - seenObjects.clear(); - } - - function _traverse (val, seen) { - var i, keys; - var isA = Array.isArray(val); - if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) { - return - } - if (val.__ob__) { - var depId = val.__ob__.dep.id; - if (seen.has(depId)) { - return - } - seen.add(depId); - } - if (isA) { - i = val.length; - while (i--) { _traverse(val[i], seen); } - } else { - keys = Object.keys(val); - i = keys.length; - while (i--) { _traverse(val[keys[i]], seen); } - } - } - - /* */ - - var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } - }); - - function createFnInvoker (fns, vm) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - invokeWithErrorHandling(cloned[i], null, arguments$1, vm, "v-on handler"); - } - } else { - // return handler return value for single handlers - return invokeWithErrorHandling(fns, null, arguments, vm, "v-on handler") - } - } - invoker.fns = fns; - return invoker - } - - function updateListeners ( - on, - oldOn, - add, - remove$$1, - createOnceHandler, - vm - ) { - var name, def$$1, cur, old, event; - for (name in on) { - def$$1 = cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - if (isUndef(cur)) { - warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur, vm); - } - if (isTrue(event.once)) { - cur = on[name] = createOnceHandler(event.name, cur, event.capture); - } - add(event.name, cur, event.capture, event.passive, event.params); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } - } - - /* */ - - function mergeVNodeHook (def, hookKey, hook) { - if (def instanceof VNode) { - def = def.data.hook || (def.data.hook = {}); - } - var invoker; - var oldHook = def[hookKey]; - - function wrappedHook () { - hook.apply(this, arguments); - // important: remove merged hook to ensure it's called only once - // and prevent memory leak - remove(invoker.fns, wrappedHook); - } - - if (isUndef(oldHook)) { - // no existing hook - invoker = createFnInvoker([wrappedHook]); - } else { - /* istanbul ignore if */ - if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { - // already a merged invoker - invoker = oldHook; - invoker.fns.push(wrappedHook); - } else { - // existing plain hook - invoker = createFnInvoker([oldHook, wrappedHook]); - } - } - - invoker.merged = true; - def[hookKey] = invoker; - } - - /* */ - - function extractPropsFromVNodeData ( - data, - Ctor, - tag - ) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(tag || Ctor)) + ", but the declared prop name is" + - " \"" + key + "\". " + - "Note that HTML attributes are case-insensitive and camelCased " + - "props need to use their kebab-case equivalents when using in-DOM " + - "templates. You should probably use \"" + altKey + "\" instead of \"" + key + "\"." - ); - } - } - checkProp(res, props, key, altKey, true) || - checkProp(res, attrs, key, altKey, false); - } - } - return res - } - - function checkProp ( - res, - hash, - key, - altKey, - preserve - ) { - if (isDef(hash)) { - if (hasOwn(hash, key)) { - res[key] = hash[key]; - if (!preserve) { - delete hash[key]; - } - return true - } else if (hasOwn(hash, altKey)) { - res[key] = hash[altKey]; - if (!preserve) { - delete hash[altKey]; - } - return true - } - } - return false - } - - /* */ - - // The template compiler attempts to minimize the need for normalization by - // statically analyzing the template at compile time. - // - // For plain HTML markup, normalization can be completely skipped because the - // generated render function is guaranteed to return Array. There are - // two cases where extra normalization is needed: - - // 1. When the children contains components - because a functional component - // may return an Array instead of a single root. In this case, just a simple - // normalization is needed - if any child is an Array, we flatten the whole - // thing with Array.prototype.concat. It is guaranteed to be only 1-level deep - // because functional components already normalize their own children. - function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.isArray(children[i])) { - return Array.prototype.concat.apply([], children) - } - } - return children - } - - // 2. When the children contains constructs that always generated nested Arrays, - // e.g.