diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 0ca515486b4..e7a6bbba964 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -634,20 +634,60 @@ as a single icon. Useful for when you want to manipulate an icon via the above a The _flatIcons list is a cache for generated icon files. */ -proc - getFlatIcon(atom/A, dir) // 1 = use cache, 2 = override cache, 0 = ignore cache +proc // Creates a single icon from a given /atom or /image. Only the first argument is required. + getFlatIcon(image/A, defdir=A.dir, deficon=A.icon, defstate=A.icon_state, defblend=A.blend_mode) + // We start with a blank canvas, otherwise some icon procs crash silently + var/icon/flat = icon('icons/effects/effects.dmi', "icon_state"="nothing") // Final flattened icon + if(!A) + return flat + if(A.alpha <= 0) + return flat + var/noIcon = FALSE + + var/curicon + if(A.icon) + curicon = A.icon + else + curicon = deficon + + if(!curicon) + noIcon = TRUE // Do not render this object. + + var/curstate + if(A.icon_state) + curstate = A.icon_state + else + curstate = defstate + + if(!noIcon && !(curstate in icon_states(curicon))) + if("" in icon_states(curicon)) + curstate = "" + else + noIcon = TRUE // Do not render this object. + + var/curdir + if(A.dir != 2) + curdir = A.dir + else + curdir = defdir + + var/curblend + if(A.blend_mode == BLEND_DEFAULT) + curblend = defblend + else + curblend = A.blend_mode + // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed var/list/layers = list() - - // Add the atom's icon itself - if(A.icon) - // Make a copy without pixel_x/y settings - var/image/copy = image(icon=A.icon,icon_state=A.icon_state,layer=A.layer,dir=A.dir) + var/image/copy + // Add the atom's icon itself, without pixel_x/y offsets. + if(!noIcon) + copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=curdir) + copy.color = A.color + copy.alpha = A.alpha + copy.blend_mode = curblend layers[copy] = A.layer - // dir defaults to A's dir - if(!dir) dir = A.dir - // Loop through the underlays, then overlays, sorting them into the layers list var/list/process = A.underlays // Current list being processed var/pSet=0 // Which list is being processed: 0 = underlays, 1 = overlays @@ -662,7 +702,7 @@ proc if(!current) continue currentLayer = current:layer if(currentLayer<0) // Special case for FLY_LAYER - if(currentLayer <= -1000) return 0 + if(currentLayer <= -1000) return flat if(pSet == 0) // Underlay currentLayer = A.layer+currentLayer/1000 else // Overlay @@ -688,8 +728,6 @@ proc else // All done break - // We start with a blank canvas, otherwise some icon procs crash silently - var/icon/flat = icon('icons/effects/effects.dmi', "icon_state"="nothing") // Final flattened icon var/icon/add // Icon of overlay being added // Current dimensions of flattened icon @@ -699,24 +737,33 @@ proc for(var/I in layers) - if(I:icon) - if(I:icon_state) - // Has icon and state set - add = icon(I:icon, I:icon_state) - else - if(A.icon_state in icon_states(I:icon)) - // Inherits icon_state from atom - add = icon(I:icon, A.icon_state) - else - // Uses default state ("") - add = icon(I:icon) - else if(I:icon_state) - // Inherits icon from atom - add = icon(A.icon, I:icon_state) - else - // Unknown + if(I:alpha == 0) continue + if(I == copy) // 'I' is an /image based on the object being flattened. + curblend = BLEND_OVERLAY + add = icon(I:icon, I:icon_state, I:dir) + // This checks for a silent failure mode of the icon routine. If the requested dir + // doesn't exist in this icon state it returns a 32x32 icon with 0 alpha. + if (I:dir != SOUTH && add.Width() == 32 && add.Height() == 32) + // Check every pixel for blank (computationally expensive, but the process is limited + // by the amount of film on the station, only happens when we hit something that's + // turned, and bails at the very first pixel it sees. + var/blankpixel; + for(var/y;y<=32;y++) + for(var/x;x<32;x++) + blankpixel = isnull(add.GetPixel(x,y)) + if(!blankpixel) + break + if(!blankpixel) + break + // If we ALWAYS returned a null (which happens when GetPixel encounters something with alpha 0) + if (blankpixel) + // Pull the default direction. + add = icon(I:icon, I:icon_state) + else // 'I' is an appearance object. + add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend) + // Find the new dimensions of the flat icon to fit the added overlay addX1 = min(flatX1, I:pixel_x+1) addX2 = max(flatX2, I:pixel_x+add.Width()) @@ -730,9 +777,14 @@ proc flatY1=addY1;flatY2=addY2 // Blend the overlay into the flattened icon - flat.Blend(add,ICON_OVERLAY,I:pixel_x+2-flatX1,I:pixel_y+2-flatY1) + flat.Blend(add, blendMode2iconMode(curblend), I:pixel_x + 2 - flatX1, I:pixel_y + 2 - flatY1) - return flat + if(A.color) + flat.Blend(A.color, ICON_MULTIPLY) + if(A.alpha < 255) + flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) + + return icon(flat, "", SOUTH) getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. @@ -783,4 +835,23 @@ proc/adjust_brightness(var/color, var/value) RGB[1] = Clamp(RGB[1]+value,0,255) RGB[2] = Clamp(RGB[2]+value,0,255) RGB[3] = Clamp(RGB[3]+value,0,255) - return rgb(RGB[1],RGB[2],RGB[3]) \ No newline at end of file + return rgb(RGB[1],RGB[2],RGB[3]) + +proc/sort_atoms_by_layer(var/list/atoms) + // Comb sort icons based on levels + var/list/result = atoms.Copy() + var/gap = result.len + var/swapped = 1 + while (gap > 1 || swapped) + swapped = 0 + if(gap > 1) + gap = round(gap / 1.3) // 1.3 is the emperic comb sort coefficient + if(gap < 1) + gap = 1 + for(var/i = 1; gap + i <= result.len; i++) + var/atom/l = result[i] //Fucking hate + var/atom/r = result[gap+i] //how lists work here + if(l.layer > r.layer) //no "result[i].layer" for me + result.Swap(i, gap + i) + swapped = 1 + return result diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index 885de82c44b..8c188d0d180 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -298,6 +298,13 @@ proc/tg_list2text(list/list, glue=",") /proc/angle2text(var/degree) return dir2text(angle2dir(degree)) +//Converts a blend_mode constant to one acceptable to icon.Blend() +/proc/blendMode2iconMode(blend_mode) + switch(blend_mode) + if(BLEND_MULTIPLY) return ICON_MULTIPLY + if(BLEND_ADD) return ICON_ADD + if(BLEND_SUBTRACT) return ICON_SUBTRACT + else return ICON_OVERLAY //Converts a rights bitfield into a string /proc/rights2text(rights,seperator="") diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 1284aa52836..2840f187543 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -298,7 +298,17 @@ // Simple helper to face what you clicked on, in case it should be needed in more than one place /mob/proc/face_atom(var/atom/A) - if( stat || (buckled && !buckled.movable) || !A || !x || !y || !A.x || !A.y ) return + + // Snowflake for space vines. + var/is_buckled = 0 + if(buckled) + if(istype(buckled)) + if(!buckled.movable) + is_buckled = 1 + else + is_buckled = 0 + + if( stat || is_buckled || !A || !x || !y || !A.x || !A.y ) return var/dx = A.x - x var/dy = A.y - y if(!dx && !dy) return diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 98da4a58ded..a1a626313b6 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -119,7 +119,7 @@ L.fields["b_dna"] = H.dna.unique_enzymes L.fields["enzymes"] = H.dna.SE // Used in respawning L.fields["identity"] = H.dna.UI // " - L.fields["image"] = getFlatIcon(H,0) //This is god-awful + L.fields["image"] = getFlatIcon(H) //This is god-awful locked += L return diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index 74da3c321fd..1e267e524f3 100755 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -266,7 +266,10 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee /obj/item/weapon/minihoe, /obj/item/device/analyzer/plant_analyzer, /obj/item/clothing/gloves/botanic_leather, - /obj/item/clothing/suit/apron) // Updated with new things + /obj/item/clothing/suit/apron, + /obj/item/weapon/minihoe, + /obj/item/weapon/storage/box/botanydisk + ) // Updated with new things cost = 15 containertype = /obj/structure/closet/crate/hydroponics containername = "Hydroponics crate" diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 28efdb90874..61dfe3d915e 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -16,6 +16,7 @@ anchored = 1 density = 1 var/active = 1 //No sales pitches if off! + var/delay_product_spawn // If set, uses sleep() in product spawn proc (mostly for seeds to retrieve correct names). var/vend_ready = 1 //Are we ready to vend?? Is it time?? var/vend_delay = 10 //How long does it take to vend? var/datum/data/vending_product/currently_vending = null // A /datum/data/vending_product instance of what we're paying for right now. @@ -111,7 +112,7 @@ var/atom/temp = new typepath(null) var/datum/data/vending_product/R = new /datum/data/vending_product() - R.product_name = temp.name + R.product_path = typepath R.amount = amount R.price = price @@ -123,6 +124,13 @@ coin_records += R else product_records += R + + if(delay_product_spawn) + sleep(1) + R.product_name = temp.name + else + R.product_name = temp.name + // world << "Added: [R.product_name]] - [R.amount] - [R.product_path]" return @@ -814,6 +822,8 @@ product_slogans = "THIS'S WHERE TH' SEEDS LIVE! GIT YOU SOME!;Hands down the best seed selection on the station!;Also certain mushroom varieties available, more for experts! Get certified today!" product_ads = "We like plants!;Grow some crops!;Grow, baby, growww!;Aw h'yeah son!" icon_state = "seeds" + delay_product_spawn = 1 + products = list(/obj/item/seeds/bananaseed = 3,/obj/item/seeds/berryseed = 3,/obj/item/seeds/carrotseed = 3,/obj/item/seeds/chantermycelium = 3,/obj/item/seeds/chiliseed = 3, /obj/item/seeds/cornseed = 3, /obj/item/seeds/eggplantseed = 3, /obj/item/seeds/potatoseed = 3, /obj/item/seeds/replicapod = 3,/obj/item/seeds/soyaseed = 3, /obj/item/seeds/sunflowerseed = 3,/obj/item/seeds/tomatoseed = 3,/obj/item/seeds/towermycelium = 3,/obj/item/seeds/wheatseed = 3,/obj/item/seeds/appleseed = 3, diff --git a/code/game/mecha/mech_sensor.dm b/code/game/mecha/mech_sensor.dm index ac8bc9a30fa..d7e1937715f 100644 --- a/code/game/mecha/mech_sensor.dm +++ b/code/game/mecha/mech_sensor.dm @@ -25,6 +25,14 @@ return 1 /obj/machinery/mech_sensor/proc/is_blocked(O as obj) + if(istype(O, /obj/mecha/medical/odysseus)) + var/obj/mecha/medical/odysseus/M = O + for(var/obj/item/mecha_parts/mecha_equipment/ME in M.equipment) + if(istype(ME, /obj/item/mecha_parts/mecha_equipment/tool/sleeper)) + var/obj/item/mecha_parts/mecha_equipment/tool/sleeper/S = ME + if(S.occupant != null) + return 0 + return istype(O, /obj/mecha) || istype(O, /obj/vehicle) /obj/machinery/mech_sensor/proc/give_feedback(O as obj) diff --git a/code/game/supplyshuttle.dm b/code/game/supplyshuttle.dm index 4fb6683bd78..1ef29b5065e 100644 --- a/code/game/supplyshuttle.dm +++ b/code/game/supplyshuttle.dm @@ -54,7 +54,10 @@ var/list/mechtoys = list( if (istype(A, /obj/structure/stool/bed) && B.buckled_mob)//if it's a bed/chair and someone is buckled, it will not pass return 0 - else if(istype(A, /mob/living)) // You Shall Not Pass! + if(istype(A, /obj/vehicle)) //no vehicles + return 0 + + if(istype(A, /mob/living)) // You Shall Not Pass! var/mob/living/M = A if(!M.lying && !istype(M, /mob/living/carbon/monkey) && !istype(M, /mob/living/carbon/slime) && !istype(M, /mob/living/simple_animal/mouse) && !istype(M, /mob/living/silicon/robot/drone)) //If your not laying down, or a small creature, no pass. return 0 diff --git a/code/modules/hydroponics/hydro_tools.dm b/code/modules/hydroponics/hydro_tools.dm index 7d2836d1012..f39fc0ea868 100644 --- a/code/modules/hydroponics/hydro_tools.dm +++ b/code/modules/hydroponics/hydro_tools.dm @@ -1,4 +1,8 @@ -//Analyzer, pestkillers, weedkillers, nutrients, hatchets. +//Analyzer, pestkillers, weedkillers, nutrients, hatchets, cutters. + +/obj/item/weapon/wirecutters/clippers + name = "plant clippers" + desc = "A tool used to take samples from plants." /obj/item/device/analyzer/plant_analyzer name = "plant analyzer" @@ -67,7 +71,9 @@ if(grown_seed.harvest_repeat) dat += "This plant can be harvested repeatedly." - if(grown_seed.immutable) + if(grown_seed.immutable == -1) + dat += "This plant is highly mutable." + else if(grown_seed.immutable > 0) dat += "This plant does not possess genetics that are alterable." if(grown_seed.products && grown_seed.products.len) diff --git a/code/modules/hydroponics/hydro_tray.dm b/code/modules/hydroponics/hydro_tray.dm index 1ead014c2af..d391e33f524 100644 --- a/code/modules/hydroponics/hydro_tray.dm +++ b/code/modules/hydroponics/hydro_tray.dm @@ -30,6 +30,7 @@ var/lastcycle = 0 // Cycle timing/tracking var. var/cycledelay = 150 // Delay per cycle. var/closed_system // If set, the tray will attempt to take atmos from a pipe. + var/force_update // Seed details/line data. var/datum/seed/seed = null // The currently planted seed @@ -117,6 +118,10 @@ /obj/machinery/portable_atmospherics/hydroponics/bullet_act(var/obj/item/projectile/Proj) + //Don't act on seeds like dionaea that shouldn't change. + if(seed && seed.immutable > 0) + return + //Override for somatoray projectiles. if(istype(Proj ,/obj/item/projectile/energy/floramut) && prob(20)) mutate(1) @@ -138,8 +143,11 @@ /obj/machinery/portable_atmospherics/hydroponics/process() // Update values every cycle rather than every process() tick. - if(world.time < (lastcycle + cycledelay)) + if(force_update) + force_update = 0 + else if(world.time < (lastcycle + cycledelay)) return + lastcycle = world.time // Weeds like water and nutrients, there's a chance the weed population will increase. @@ -159,7 +167,12 @@ return // Advance plant age. - age += 1 * HYDRO_SPEED_MULTIPLIER + if(prob(25)) age += 1 * HYDRO_SPEED_MULTIPLIER + + //Highly mutable plants have a chance of mutating every tick. + if(seed.immutable == -1) + var/mut_prob = rand(1,100) + if(mut_prob <= 5) mutate(mut_prob == 1 ? 2 : 1) // Maintain tray nutrient and water levels. if(seed.nutrient_consumption > 0 && nutrilevel > 0 && prob(25)) @@ -188,10 +201,23 @@ // If atmos input is not there, grab from turf. if(!environment) if(istype(T)) - environment = T.return_air() - if(!environment) //We're in a crate or nullspace, bail out. + environment = T.air + if(!environment) return + // Handle gas consumption. + if(seed.consume_gasses && seed.consume_gasses.len) + var/missing_gas = 0 + for(var/gas in seed.consume_gasses) + if(environment && environment.gas && environment.gas[gas] && \ + environment.gas[gas] >= seed.consume_gasses[gas]) + environment.adjust_gas(gas,-seed.consume_gasses[gas],1) + else + missing_gas++ + + if(missing_gas > 0) + health -= missing_gas * HYDRO_SPEED_MULTIPLIER + // Process it. var/pressure = environment.return_pressure() if(pressure < seed.lowkpa_tolerance || pressure > seed.highkpa_tolerance) @@ -200,6 +226,13 @@ if(abs(environment.temperature - seed.ideal_heat) > seed.heat_tolerance) health -= healthmod + // Handle gas production. + if(seed.exude_gasses && seed.exude_gasses.len) + var/datum/gas_mixture/exuded = new + for(var/gas in seed.exude_gasses) + exuded.adjust_gas(gas,seed.exude_gasses[gas*seed.potency],1) //This will need adjustment since it produces moles. + loc.assume_air(exuded) + // Handle light requirements. var/area/A = T.loc if(A) @@ -249,13 +282,14 @@ pestlevel = 0 // If enough time (in cycles, not ticks) has passed since the plant was harvested, we're ready to harvest again. - else if(age > seed.production && (age - lastproduce) > seed.production && (!harvest && !dead)) + else if(seed.products && seed.products.len && age > seed.production && \ + (age - lastproduce) > seed.production && (!harvest && !dead)) + harvest = 1 lastproduce = age if(prob(5)) // On each tick, there's a 5 percent chance the pest population will increase pestlevel += 1 * HYDRO_SPEED_MULTIPLIER - check_level_sanity() update_icon() return @@ -385,7 +419,7 @@ return // Check if we should even bother working on the current seed datum. - if(seed.mutants.len && severity > 1 && prob(10+mutation_mod)) + if(seed.mutants. && seed.mutants.len && severity > 1 && prob(10+mutation_mod)) mutate_species() return @@ -437,7 +471,22 @@ /obj/machinery/portable_atmospherics/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob) - if (istype(O, /obj/item/weapon/reagent_containers/glass)) + if(istype(O, /obj/item/weapon/wirecutters) || istype(O, /obj/item/weapon/scalpel)) + + if(!seed) + user << "There is nothing to take a sample from in \the [src]." + return + + seed.harvest(user,yield_mod,1) + health -= (rand(1,5)*10) + check_level_sanity() + + force_update = 1 + process() + + return + + else if (istype(O, /obj/item/weapon/reagent_containers/glass)) var/b_amount = O.reagents.get_reagent_amount("water") if(b_amount > 0 && waterlevel < 100) if(b_amount + waterlevel > 100) @@ -573,7 +622,7 @@ reagent_value = mutagenic_reagents[R.id]+mutation_mod if(reagent_total >= reagent_value) if(prob(min(reagent_total*reagent_value,100))) - mutate(reagent_value > 10 ? 2 : 1) + mutate(reagent_total > 10 ? 2 : 1) S.reagents.clear_reagents() @@ -613,7 +662,8 @@ seed = S.seed //Grab the seed datum. dead = 0 age = 1 - health = seed.endurance + //Snowflakey, maybe move this to the seed datum + health = (istype(S, /obj/item/seeds/cutting) ? round(seed.endurance/rand(2,5)) : seed.endurance) lastcycle = world.time del(O) @@ -622,7 +672,7 @@ update_icon() else - user << "\red The [src] already has seeds in it!" + user << "\red \The [src] already has seeds in it!" else if (istype(O, /obj/item/weapon/reagent_containers/spray/plantbgone)) if(seed) @@ -733,7 +783,30 @@ usr << "[src] is \red filled with weeds!" if(pestlevel >= 5) usr << "[src] is \red filled with tiny worms!" - usr << text ("") + if(!istype(src,/obj/machinery/portable_atmospherics/hydroponics/soil)) + + var/turf/T = loc + var/datum/gas_mixture/environment + + if(closed_system && (connected_port || holding)) + environment = air_contents + + if(!environment) + if(istype(T)) + environment = T.return_air() + + if(!environment) //We're in a crate or nullspace, bail out. + return + + var/area/A = T.loc + var/light_available + if(A) + if(A.lighting_use_dynamic) + light_available = max(0,min(10,T.lighting_lumcount)-5) + else + light_available = 5 + + usr << "The tray's sensor suite is reporting a light level of [light_available] lumens and a temperature of [environment.temperature]K." /obj/machinery/portable_atmospherics/hydroponics/verb/close_lid() set name = "Toggle Tray Lid" diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm index 0c87e6dcdd2..fa5af7c78c7 100644 --- a/code/modules/hydroponics/seed_datums.dm +++ b/code/modules/hydroponics/seed_datums.dm @@ -58,9 +58,9 @@ proc/populate_seed_list() //Tolerances. var/requires_nutrients = 1 // The plant can starve. - var/nutrient_consumption = 0.1 // Plant eats this much per tick. + var/nutrient_consumption = 0.25 // Plant eats this much per tick. var/requires_water = 1 // The plant can become dehydrated. - var/water_consumption = 1 // Plant drinks this much per tick. + var/water_consumption = 3 // Plant drinks this much per tick. var/ideal_heat = 293 // Preferred temperature in Kelvin. var/heat_tolerance = 20 // Departure from ideal that is survivable. var/ideal_light = 8 // Preferred light level in luminosity. @@ -83,7 +83,7 @@ proc/populate_seed_list() var/spread = 0 // 0 limits plant to tray, 1 = creepers, 2 = vines. var/carnivorous = 0 // 0 = none, 1 = eat pests in tray, 2 = eat living things (when a vine). var/parasite = 0 // 0 = no, 1 = gain health from weed level. - var/immutable // If set, plant will never mutate. + var/immutable = 0 // If set, plant will never mutate. If -1, plant has a chance of mutating during process(). var/alter_temp // If set, the plant will periodically alter local temp by this amount. // Cosmetics. @@ -99,14 +99,15 @@ proc/populate_seed_list() //Returns a key corresponding to an entry in the global seed list. /datum/seed/proc/get_mutant_variant() - if(!mutants || !mutants.len || immutable) return 0 + if(!mutants || !mutants.len || immutable > 0) return 0 return pick(mutants) //Mutates the plant overall (randomly). /datum/seed/proc/mutate(var/degree,var/turf/source_turf) - if(!degree || immutable) return - source_turf.visible_message("[display_name] quivers uneasily!") + if(!degree || immutable > 0) return + + source_turf.visible_message("\blue \The [display_name] quivers!") //This looks like shit, but it's a lot easier to read/change this way. var/total_mutations = rand(1,1+degree) @@ -115,7 +116,7 @@ proc/populate_seed_list() if(0) //Plant cancer! lifespan = max(0,lifespan-rand(1,5)) endurance = max(0,endurance-rand(10,20)) - source_turf.visible_message("[display_name] withers rapidly!") + source_turf.visible_message("\red \The [display_name] withers rapidly!") if(1) nutrient_consumption = max(0, min(5, nutrient_consumption + rand(-(degree*0.1),(degree*0.1)))) water_consumption = max(0, min(50, water_consumption + rand(-degree,degree))) @@ -134,7 +135,7 @@ proc/populate_seed_list() if(prob(degree*5)) carnivorous = max(0, min(2, carnivorous + rand(-degree,degree))) if(carnivorous) - source_turf.visible_message("[display_name] shudders hungrily.") + source_turf.visible_message("\blue \The [display_name] shudders hungrily.") if(6) weed_tolerance = max(0, min(10, weed_tolerance + (rand(-2,2) * degree))) if(prob(degree*5)) parasite = !parasite @@ -148,7 +149,7 @@ proc/populate_seed_list() potency = max(0, min(200, potency + (rand(-20,20) * degree))) if(prob(degree*5)) spread = max(0, min(2, spread + rand(-1,1))) - source_turf.visible_message("[display_name] spasms visibly, shifting in the tray.") + source_turf.visible_message("\blue \The [display_name] spasms visibly, shifting in the tray.") if(9) maturation = max(0, min(30, maturation + (rand(-1,1) * degree))) if(prob(degree*5)) @@ -157,28 +158,28 @@ proc/populate_seed_list() if(prob(degree*2)) biolum = !biolum if(biolum) - source_turf.visible_message("[display_name] begins to glow!") + source_turf.visible_message("\blue \The [display_name] begins to glow!") if(prob(degree*2)) biolum_colour = "#[pick(list("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF"))]" - source_turf.visible_message("[display_name]'s glow changes colour!") + source_turf.visible_message("\blue \The [display_name]'s glow changes colour!") else - source_turf.visible_message("[display_name]'s glow dims...") + source_turf.visible_message("\blue \The [display_name]'s glow dims...") if(11) if(prob(degree*2)) flowers = !flowers if(flowers) - source_turf.visible_message("[display_name] sprouts a bevy of flowers!") + source_turf.visible_message("\blue \The [display_name] sprouts a bevy of flowers!") if(prob(degree*2)) flower_colour = "#[pick(list("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF"))]" - source_turf.visible_message("[display_name]'s flowers changes colour!") + source_turf.visible_message("\blue \The [display_name]'s flowers changes colour!") else - source_turf.visible_message("[display_name]'s flowers wither and fall off.") + source_turf.visible_message("\blue \The [display_name]'s flowers wither and fall off.") return //Mutates a specific trait/set of traits. /datum/seed/proc/apply_gene(var/datum/plantgene/gene) - if(!gene || !gene.values || immutable) return + if(!gene || !gene.values || immutable > 0) return switch(gene.genetype) @@ -208,11 +209,12 @@ proc/populate_seed_list() else chems[rid] = gene.values[2][rid] - //TODO. - //if(!exude_gasses) exude_gasses = list() - //exude_gasses |= gene.values[3] - //for(var/gas in exude_gasses) - // exude_gasses[gas] = max(1,round(exude_gasses[gas]/2)) + var/list/new_gasses = gene.values[3] + if(istype(new_gasses)) + if(!exude_gasses) exude_gasses = list() + exude_gasses |= new_gasses + for(var/gas in exude_gasses) + exude_gasses[gas] = max(1,round(exude_gasses[gas]*0.8)) alter_temp = gene.values[4] potency = gene.values[5] @@ -343,11 +345,10 @@ proc/populate_seed_list() return (P ? P : 0) //Place the plant products at the feet of the user. -/datum/seed/proc/harvest(var/mob/user,var/yield_mod) +/datum/seed/proc/harvest(var/mob/user,var/yield_mod,var/harvest_sample) if(!user) return - //TODO: check for failing to harvest. var/got_product if(!isnull(products) && products.len && yield > 0) got_product = 1 @@ -355,7 +356,7 @@ proc/populate_seed_list() if(!got_product) user << "\red You fail to harvest anything useful." else - user << "You harvest from the [display_name]." + user << "You [harvest_sample ? "take a sample" : "harvest"] from the [display_name]." //This may be a new line. Update the global if it is. if(name == "new line" || !(name in seed_types)) @@ -363,12 +364,18 @@ proc/populate_seed_list() name = "[uid]" seed_types[name] = src + if(harvest_sample) + var/obj/item/seeds/seeds = new(get_turf(user)) + seeds.seed_type = name + seeds.update_seed() + return + var/total_yield if(isnull(yield_mod) || yield_mod < 1) yield_mod = 0 total_yield = yield else - total_yield = max(1,rand(1,((yield_mod+yield)))) + total_yield = max(1,rand(yield_mod,yield_mod+yield)) currently_querying = list() for(var/i = 0;i 0) return //Set up some basic information. var/datum/seed/new_seed = new @@ -1163,6 +1169,7 @@ proc/populate_seed_list() yield = -1 potency = -1 growth_stages = 4 + immutable = -1 /datum/seed/whitebeets name = "whitebeet" @@ -1400,4 +1407,4 @@ proc/populate_seed_list() maturation = 1 production = 1 yield = 1 - potency = 1 \ No newline at end of file + potency = 1 diff --git a/code/modules/hydroponics/seed_machines.dm b/code/modules/hydroponics/seed_machines.dm index e9d02a44fca..837abe7211a 100644 --- a/code/modules/hydroponics/seed_machines.dm +++ b/code/modules/hydroponics/seed_machines.dm @@ -3,9 +3,16 @@ desc = "A small disk used for carrying data on plant genetics." icon = 'icons/obj/hydroponics.dmi' icon_state = "disk" + w_class = 1.0 + var/list/genes = list() var/genesource = "unknown" +/obj/item/weapon/disk/botany/New() + ..() + pixel_x = rand(-5,5) + pixel_y = rand(-5,5) + /obj/item/weapon/disk/botany/attack_self(var/mob/user as mob) if(genes.len) var/choice = alert(user, "Are you sure you want to wipe the disk?", "Xenobotany Data", "No", "Yes") @@ -24,6 +31,7 @@ ..() for(var/i = 0;i<7;i++) new /obj/item/weapon/disk/botany(src) + /obj/machinery/botany icon = 'icons/obj/hydroponics.dmi' icon_state = "hydrotray3" @@ -77,16 +85,18 @@ /obj/machinery/botany/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W,/obj/item/seeds)) if(seed) - if(seed.seed.immutable) - user << "That seed is not compatible with our genetics technology." - else - user << "There is already a seed loaded." + user << "There is already a seed loaded." + + var/obj/item/seeds/S =W + if(S.seed && S.seed.immutable > 0) + user << "That seed is not compatible with our genetics technology." else user.drop_item(W) W.loc = src seed = W user << "You load [W] into [src]." return + if(istype(W,/obj/item/weapon/screwdriver)) open = !open user << "\blue You [open ? "open" : "close"] the maintenance panel." @@ -181,7 +191,7 @@ if(seed.seed.name == "new line" || isnull(seed_types[seed.seed.name])) seed.seed.uid = seed_types.len + 1 - seed.seed.name = "[uid]" + seed.seed.name = "[seed.seed.uid]" seed_types[seed.seed.name] = seed.seed seed.update_seed() @@ -215,6 +225,8 @@ if(seed && seed.seed) genetics = seed.seed + degradation = 0 + del(seed) seed = null @@ -233,7 +245,7 @@ if(!genetics.roundstart) loaded_disk.genesource += " (variety #[genetics.uid])" - loaded_disk.name += " ([gene_tag_masks[href_list["get_gene"]]])" + loaded_disk.name += " ([gene_tag_masks[href_list["get_gene"]]], #[genetics.uid])" loaded_disk.desc += " The label reads \'gene [gene_tag_masks[href_list["get_gene"]]], sampled from [genetics.display_name]\'." eject_disk = 1 diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm index dbc13158ec3..d0afda2e6fb 100644 --- a/code/modules/hydroponics/seeds.dm +++ b/code/modules/hydroponics/seeds.dm @@ -32,6 +32,14 @@ if(seed && !seed.roundstart) usr << "It's tagged as variety #[seed.uid]." +/obj/item/seeds/cutting + name = "cuttings" + desc = "Some plant cuttings." + +/obj/item/seeds/cutting/update_appearance() + ..() + src.name = "packet of [seed.seed_name] cuttings" + /obj/item/seeds/replicapod seed_type = "diona" diff --git a/code/modules/hydroponics/vines.dm b/code/modules/hydroponics/vines.dm index a7312e3dc1a..085229d417c 100644 --- a/code/modules/hydroponics/vines.dm +++ b/code/modules/hydroponics/vines.dm @@ -295,8 +295,8 @@ var/limited_growth = 0 /obj/effect/plant_controller/creeper - collapse_limit = 50 - slowdown_limit = 5 + collapse_limit = 6 + slowdown_limit = 3 limited_growth = 1 /obj/effect/plant_controller/New() diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 1fa7e6349d8..5a06f2cfbea 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -251,7 +251,7 @@ proc/get_damage_icon_part(damage_state, body_part) icon_key = "[icon_key]0" else if(part.status & ORGAN_ROBOT) icon_key = "[icon_key]2" - else if(part.status & ORGAN_DEAD) //Do we even have necrosis in our current code? ~Z + else if(part.status & ORGAN_DEAD) icon_key = "[icon_key]3" else icon_key = "[icon_key]1" @@ -274,6 +274,10 @@ proc/get_damage_icon_part(damage_state, body_part) //No icon stored, so we need to start with a basic one. var/datum/organ/external/chest = get_organ("chest") base_icon = chest.get_icon(g) + + if(chest.status & ORGAN_DEAD) + base_icon.ColorTone(necrosis_color_mod) + base_icon.SetIntensity(0.7) for(var/datum/organ/external/part in organs) diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index d713390fd4e..fe2326c21b0 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -53,10 +53,10 @@ /obj/item/weapon/photo/proc/show(mob/user as mob) user << browse_rsc(img, "tmp_photo.png") user << browse("[name]" \ - + "" \ - + " Written on the back:[scribble]" : ]"\ - + "", "window=book;size=200x[scribble ? 400 : 200]") + + "" \ + + "" \ + + "[scribble ? "Written on the back:[scribble]" : ""]"\ + + "