diff --git a/baystation12.dme b/baystation12.dme index 92d393a37e..a8eeac7aed 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -622,7 +622,6 @@ #include "code\game\objects\items\stacks\nanopaste.dm" #include "code\game\objects\items\stacks\rods.dm" #include "code\game\objects\items\stacks\stack.dm" -#include "code\game\objects\items\stacks\sheets\glass.dm" #include "code\game\objects\items\stacks\sheets\leather.dm" #include "code\game\objects\items\stacks\sheets\light.dm" #include "code\game\objects\items\stacks\tiles\light.dm" diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm index f6d2481468..922275bcc9 100644 --- a/code/ZAS/Controller.dm +++ b/code/ZAS/Controller.dm @@ -281,8 +281,8 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun var/direct = !(block & ZONE_BLOCKED) var/space = !istype(B) - if(!space) - if(min(A.zone.contents.len, B.zone.contents.len) < ZONE_MIN_SIZE || (direct && (equivalent_pressure(A.zone,B.zone) || current_cycle == 0))) + if(direct && !space) + if(min(A.zone.contents.len, B.zone.contents.len) <= ZONE_MIN_SIZE || equivalent_pressure(A.zone,B.zone) || current_cycle == 0) merge(A.zone,B.zone) return diff --git a/code/datums/mind.dm b/code/datums/mind.dm index e5e9370da3..fac5c75fc3 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -29,7 +29,7 @@ */ -datum/mind +/datum/mind var/key var/name //replaces mob/var/original_name var/mob/living/current @@ -58,459 +58,389 @@ datum/mind // the world.time since the mob has been brigged, or -1 if not at all var/brigged_since = -1 - New(var/key) - src.key = key - //put this here for easier tracking ingame var/datum/money_account/initial_account - proc/transfer_to(mob/living/new_character) - if(!istype(new_character)) - world.log << "## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn" - if(current) //remove ourself from our old body's mind variable - if(changeling) - current.remove_changeling_powers() - current.verbs -= /datum/changeling/proc/EvolutionMenu - current.mind = null +/datum/mind/New(var/key) + src.key = key - nanomanager.user_transferred(current, new_character) // transfer active NanoUI instances to new user - if(new_character.mind) //remove any mind currently in our new body's mind variable - new_character.mind.current = null - - current = new_character //link ourself to our new body - new_character.mind = src //and link our new body to ourself +/datum/mind/proc/transfer_to(mob/living/new_character) + if(!istype(new_character)) + world.log << "## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn" + if(current) //remove ourself from our old body's mind variable if(changeling) - new_character.make_changeling() + current.remove_changeling_powers() + current.verbs -= /datum/changeling/proc/EvolutionMenu + current.mind = null - if(active) - new_character.key = key //now transfer the key to link the client to our new body + nanomanager.user_transferred(current, new_character) // transfer active NanoUI instances to new user + if(new_character.mind) //remove any mind currently in our new body's mind variable + new_character.mind.current = null - proc/store_memory(new_text) - memory += "[new_text]
" + current = new_character //link ourself to our new body + new_character.mind = src //and link our new body to ourself - proc/show_memory(mob/recipient) - var/output = "[current.real_name]'s Memory
" - output += memory + if(changeling) + new_character.make_changeling() - if(objectives.len>0) - output += "
Objectives:" + if(active) + new_character.key = key //now transfer the key to link the client to our new body - var/obj_count = 1 - for(var/datum/objective/objective in objectives) - output += "Objective #[obj_count]: [objective.explanation_text]" - obj_count++ +/datum/mind/proc/store_memory(new_text) + memory += "[new_text]
" - recipient << browse(output,"window=memory") +/datum/mind/proc/show_memory(mob/recipient) + var/output = "[current.real_name]'s Memory
" + output += memory - proc/edit_memory() - if(!ticker || !ticker.mode) - alert("Not before round-start!", "Alert") - return + if(objectives.len>0) + output += "
Objectives:" - var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
" - out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
" - out += "Assigned role: [assigned_role]. Edit
" - out += "
" - out += "Factions and special roles:
" - for(var/antag_type in all_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - out += "[antag.get_panel_entry(src)]" - out += "

" - out += "Objectives
" + var/obj_count = 1 + for(var/datum/objective/objective in objectives) + output += "Objective #[obj_count]: [objective.explanation_text]" + obj_count++ - if(objectives && objectives.len) - var/num = 1 - for(var/datum/objective/O in objectives) - out += "Objective #[num]: [O.explanation_text] " - if(O.completed) - out += "(complete)" - else - out += "(incomplete)" - out += " \[toggle\]" - out += " \[remove\]
" - num++ - out += "
\[announce objectives\]" + recipient << browse(output,"window=memory") - else - out += "None." - out += "
\[add\]" - usr << browse(out, "window=edit_memory[src]") +/datum/mind/proc/edit_memory() + if(!ticker || !ticker.mode) + alert("Not before round-start!", "Alert") + return - Topic(href, href_list) - if(!check_rights(R_ADMIN)) return + var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
" + out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
" + out += "Assigned role: [assigned_role]. Edit
" + out += "
" + out += "Factions and special roles:
" + for(var/antag_type in all_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + out += "[antag.get_panel_entry(src)]" + out += "

" + out += "Objectives
" - if(href_list["add_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["add_antagonist"]] - if(antag) antag.add_antagonist(src) - - else if(href_list["remove_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["remove_antagonist"]] - if(antag) antag.remove_antagonist(src) - - else if(href_list["equip_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["equip_antagonist"]] - if(antag) antag.equip(src.current) - - else if(href_list["unequip_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["unequip_antagonist"]] - if(antag) antag.unequip(src.current) - - else if(href_list["move_antag_to_spawn"]) - var/datum/antagonist/antag = all_antag_types[href_list["move_antag_to_spawn"]] - if(antag) antag.place_mob(src.current) - - else if (href_list["role_edit"]) - var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in joblist - if (!new_role) return - assigned_role = new_role - - else if (href_list["memory_edit"]) - var/new_memo = sanitize(input("Write new memory", "Memory", memory) as null|message) - if (isnull(new_memo)) return - memory = new_memo - - else if (href_list["obj_edit"] || href_list["obj_add"]) - var/datum/objective/objective - var/objective_pos - var/def_value - - if (href_list["obj_edit"]) - objective = locate(href_list["obj_edit"]) - if (!objective) return - objective_pos = objectives.Find(objective) - - //Text strings are easy to manipulate. Revised for simplicity. - var/temp_obj_type = "[objective.type]"//Convert path into a text string. - def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. - if(!def_value)//If it's a custom objective, it will be an empty string. - def_value = "custom" - - var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "debrain", "protect", "prevent", "harm", "brig", "hijack", "escape", "survive", "steal", "download", "mercenary", "capture", "absorb", "custom") - if (!new_obj_type) return - - var/datum/objective/new_objective = null - - switch (new_obj_type) - if ("assassinate","protect","debrain", "harm", "brig") - //To determine what to name the objective in explanation text. - var/objective_type_capital = uppertext(copytext(new_obj_type, 1,2))//Capitalize first letter. - var/objective_type_text = copytext(new_obj_type, 2)//Leave the rest of the text. - var/objective_type = "[objective_type_capital][objective_type_text]"//Add them together into a text string. - - var/list/possible_targets = list("Free objective") - for(var/datum/mind/possible_target in ticker.minds) - if ((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) - possible_targets += possible_target.current - - var/mob/def_target = null - var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain) - if (objective&&(objective.type in objective_list) && objective:target) - def_target = objective:target.current - - var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets - if (!new_target) return - - var/objective_path = text2path("/datum/objective/[new_obj_type]") - if (new_target == "Free objective") - new_objective = new objective_path - new_objective.owner = src - new_objective:target = null - new_objective.explanation_text = "Free objective" - else - new_objective = new objective_path - new_objective.owner = src - new_objective:target = new_target:mind - //Will display as special role if the target is set as MODE. Ninjas/commandos/nuke ops. - new_objective.explanation_text = "[objective_type] [new_target:real_name], the [new_target:mind:assigned_role=="MODE" ? (new_target:mind:special_role) : (new_target:mind:assigned_role)]." - - if ("prevent") - new_objective = new /datum/objective/block - new_objective.owner = src - - if ("hijack") - new_objective = new /datum/objective/hijack - new_objective.owner = src - - if ("escape") - new_objective = new /datum/objective/escape - new_objective.owner = src - - if ("survive") - new_objective = new /datum/objective/survive - new_objective.owner = src - - if ("mercenary") - new_objective = new /datum/objective/nuclear - new_objective.owner = src - - if ("steal") - if (!istype(objective, /datum/objective/steal)) - new_objective = new /datum/objective/steal - new_objective.owner = src - else - new_objective = objective - var/datum/objective/steal/steal = new_objective - if (!steal.select_target()) - return - - if("download","capture","absorb") - var/def_num - if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) - def_num = objective.target_amount - - var/target_number = input("Input target number:", "Objective", def_num) as num|null - if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. - return - - switch(new_obj_type) - if("download") - new_objective = new /datum/objective/download - new_objective.explanation_text = "Download [target_number] research levels." - if("capture") - new_objective = new /datum/objective/capture - new_objective.explanation_text = "Accumulate [target_number] capture points." - if("absorb") - new_objective = new /datum/objective/absorb - new_objective.explanation_text = "Absorb [target_number] compatible genomes." - new_objective.owner = src - new_objective.target_amount = target_number - - if ("custom") - var/expl = sanitize(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null) - if (!expl) return - new_objective = new /datum/objective - new_objective.owner = src - new_objective.explanation_text = expl - - if (!new_objective) return - - if (objective) - objectives -= objective - objectives.Insert(objective_pos, new_objective) + if(objectives && objectives.len) + var/num = 1 + for(var/datum/objective/O in objectives) + out += "Objective #[num]: [O.explanation_text] " + if(O.completed) + out += "(complete)" else - objectives += new_objective + out += "(incomplete)" + out += " \[toggle\]" + out += " \[remove\]
" + num++ + out += "
\[announce objectives\]" - else if (href_list["obj_delete"]) - var/datum/objective/objective = locate(href_list["obj_delete"]) - if(!istype(objective)) return - objectives -= objective + else + out += "None." + out += "
\[add\]" + usr << browse(out, "window=edit_memory[src]") - else if(href_list["obj_completed"]) - var/datum/objective/objective = locate(href_list["obj_completed"]) - if(!istype(objective)) return - objective.completed = !objective.completed +/datum/mind/Topic(href, href_list) + if(!check_rights(R_ADMIN)) return - else if(href_list["implant"]) - var/mob/living/carbon/human/H = current + if(href_list["add_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["add_antagonist"]] + if(antag) antag.add_antagonist(src) - BITSET(H.hud_updateflag, IMPLOYAL_HUD) // updates that players HUD images so secHUD's pick up they are implanted or not. + else if(href_list["remove_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["remove_antagonist"]] + if(antag) antag.remove_antagonist(src) - switch(href_list["implant"]) - if("remove") - for(var/obj/item/weapon/implant/loyalty/I in H.contents) - for(var/obj/item/organ/external/organs in H.organs) - if(I in organs.implants) - qdel(I) - break - H << "Your loyalty implant has been deactivated." - log_admin("[key_name_admin(usr)] has de-loyalty implanted [current].") - if("add") - H << "You somehow have become the recepient of a loyalty transplant, and it just activated!" - H.implant_loyalty(H, override = TRUE) - log_admin("[key_name_admin(usr)] has loyalty implanted [current].") + else if(href_list["equip_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["equip_antagonist"]] + if(antag) antag.equip(src.current) + + else if(href_list["unequip_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["unequip_antagonist"]] + if(antag) antag.unequip(src.current) + + else if(href_list["move_antag_to_spawn"]) + var/datum/antagonist/antag = all_antag_types[href_list["move_antag_to_spawn"]] + if(antag) antag.place_mob(src.current) + + else if (href_list["role_edit"]) + var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in joblist + if (!new_role) return + assigned_role = new_role + + else if (href_list["memory_edit"]) + var/new_memo = sanitize(input("Write new memory", "Memory", memory) as null|message) + if (isnull(new_memo)) return + memory = new_memo + + else if (href_list["obj_edit"] || href_list["obj_add"]) + var/datum/objective/objective + var/objective_pos + var/def_value + + if (href_list["obj_edit"]) + objective = locate(href_list["obj_edit"]) + if (!objective) return + objective_pos = objectives.Find(objective) + + //Text strings are easy to manipulate. Revised for simplicity. + var/temp_obj_type = "[objective.type]"//Convert path into a text string. + def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. + if(!def_value)//If it's a custom objective, it will be an empty string. + def_value = "custom" + + var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "debrain", "protect", "prevent", "harm", "brig", "hijack", "escape", "survive", "steal", "download", "mercenary", "capture", "absorb", "custom") + if (!new_obj_type) return + + var/datum/objective/new_objective = null + + switch (new_obj_type) + if ("assassinate","protect","debrain", "harm", "brig") + //To determine what to name the objective in explanation text. + var/objective_type_capital = uppertext(copytext(new_obj_type, 1,2))//Capitalize first letter. + var/objective_type_text = copytext(new_obj_type, 2)//Leave the rest of the text. + var/objective_type = "[objective_type_capital][objective_type_text]"//Add them together into a text string. + + var/list/possible_targets = list("Free objective") + for(var/datum/mind/possible_target in ticker.minds) + if ((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) + possible_targets += possible_target.current + + var/mob/def_target = null + var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain) + if (objective&&(objective.type in objective_list) && objective:target) + def_target = objective:target.current + + var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets + if (!new_target) return + + var/objective_path = text2path("/datum/objective/[new_obj_type]") + if (new_target == "Free objective") + new_objective = new objective_path + new_objective.owner = src + new_objective:target = null + new_objective.explanation_text = "Free objective" else - /* - else if (href_list["monkey"]) - var/mob/living/L = current - if (L.transforming) - return - switch(href_list["monkey"]) - if("healthy") - if (usr.client.holder.rights & R_ADMIN) - var/mob/living/carbon/human/H = current - var/mob/living/carbon/monkey/M = current - if (istype(H)) - log_admin("[key_name(usr)] attempting to monkeyize [key_name(current)]") - message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(current)]") - src = null - M = H.monkeyize() - src = M.mind - //world << "DEBUG: \"healthy\": M=[M], M.mind=[M.mind], src=[src]!" - else if (istype(M) && length(M.viruses)) - for(var/datum/disease/D in M.viruses) - D.cure(0) - sleep(0) //because deleting of virus is done through spawn(0) - if("infected") - if (usr.client.holder.rights & R_ADMIN) - var/mob/living/carbon/human/H = current - var/mob/living/carbon/monkey/M = current - if (istype(H)) - log_admin("[key_name(usr)] attempting to monkeyize and infect [key_name(current)]") - message_admins("[key_name_admin(usr)] attempting to monkeyize and infect [key_name_admin(current)]", 1) - src = null - M = H.monkeyize() - src = M.mind - current.contract_disease(new /datum/disease/jungle_fever,1,0) - else if (istype(M)) - current.contract_disease(new /datum/disease/jungle_fever,1,0) - if("human") - var/mob/living/carbon/monkey/M = current - if (istype(M)) - for(var/datum/disease/D in M.viruses) - if (istype(D,/datum/disease/jungle_fever)) - D.cure(0) - sleep(0) //because deleting of virus is doing throught spawn(0) - log_admin("[key_name(usr)] attempting to humanize [key_name(current)]") - message_admins("[key_name_admin(usr)] attempting to humanize [key_name_admin(current)]") - var/obj/item/weapon/dnainjector/m2h/m2h = new - var/obj/item/weapon/implant/mobfinder = new(M) //hack because humanizing deletes mind --rastaf0 - src = null - m2h.inject(M) - src = mobfinder.loc:mind - qdel(mobfinder) - current.radiation -= 50 - */ - else if (href_list["silicon"]) - BITSET(current.hud_updateflag, SPECIALROLE_HUD) - switch(href_list["silicon"]) + new_objective = new objective_path + new_objective.owner = src + new_objective:target = new_target:mind + //Will display as special role if the target is set as MODE. Ninjas/commandos/nuke ops. + new_objective.explanation_text = "[objective_type] [new_target:real_name], the [new_target:mind:assigned_role=="MODE" ? (new_target:mind:special_role) : (new_target:mind:assigned_role)]." - if("unemag") - var/mob/living/silicon/robot/R = current - if (istype(R)) + if ("prevent") + new_objective = new /datum/objective/block + new_objective.owner = src + + if ("hijack") + new_objective = new /datum/objective/hijack + new_objective.owner = src + + if ("escape") + new_objective = new /datum/objective/escape + new_objective.owner = src + + if ("survive") + new_objective = new /datum/objective/survive + new_objective.owner = src + + if ("mercenary") + new_objective = new /datum/objective/nuclear + new_objective.owner = src + + if ("steal") + if (!istype(objective, /datum/objective/steal)) + new_objective = new /datum/objective/steal + new_objective.owner = src + else + new_objective = objective + var/datum/objective/steal/steal = new_objective + if (!steal.select_target()) + return + + if("download","capture","absorb") + var/def_num + if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) + def_num = objective.target_amount + + var/target_number = input("Input target number:", "Objective", def_num) as num|null + if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. + return + + switch(new_obj_type) + if("download") + new_objective = new /datum/objective/download + new_objective.explanation_text = "Download [target_number] research levels." + if("capture") + new_objective = new /datum/objective/capture + new_objective.explanation_text = "Accumulate [target_number] capture points." + if("absorb") + new_objective = new /datum/objective/absorb + new_objective.explanation_text = "Absorb [target_number] compatible genomes." + new_objective.owner = src + new_objective.target_amount = target_number + + if ("custom") + var/expl = sanitize(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null) + if (!expl) return + new_objective = new /datum/objective + new_objective.owner = src + new_objective.explanation_text = expl + + if (!new_objective) return + + if (objective) + objectives -= objective + objectives.Insert(objective_pos, new_objective) + else + objectives += new_objective + + else if (href_list["obj_delete"]) + var/datum/objective/objective = locate(href_list["obj_delete"]) + if(!istype(objective)) return + objectives -= objective + + else if(href_list["obj_completed"]) + var/datum/objective/objective = locate(href_list["obj_completed"]) + if(!istype(objective)) return + objective.completed = !objective.completed + + else if(href_list["implant"]) + var/mob/living/carbon/human/H = current + + BITSET(H.hud_updateflag, IMPLOYAL_HUD) // updates that players HUD images so secHUD's pick up they are implanted or not. + + switch(href_list["implant"]) + if("remove") + for(var/obj/item/weapon/implant/loyalty/I in H.contents) + for(var/obj/item/organ/external/organs in H.organs) + if(I in organs.implants) + qdel(I) + break + H << "Your loyalty implant has been deactivated." + log_admin("[key_name_admin(usr)] has de-loyalty implanted [current].") + if("add") + H << "You somehow have become the recepient of a loyalty transplant, and it just activated!" + H.implant_loyalty(H, override = TRUE) + log_admin("[key_name_admin(usr)] has loyalty implanted [current].") + else + else if (href_list["silicon"]) + BITSET(current.hud_updateflag, SPECIALROLE_HUD) + switch(href_list["silicon"]) + + if("unemag") + var/mob/living/silicon/robot/R = current + if (istype(R)) + R.emagged = 0 + if (R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + log_admin("[key_name_admin(usr)] has unemag'ed [R].") + + if("unemagcyborgs") + if (istype(current, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/ai = current + for (var/mob/living/silicon/robot/R in ai.connected_robots) R.emagged = 0 - if (R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - log_admin("[key_name_admin(usr)] has unemag'ed [R].") + if (R.module) + if (R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + log_admin("[key_name_admin(usr)] has unemag'ed [ai]'s Cyborgs.") - if("unemagcyborgs") - if (istype(current, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/ai = current - for (var/mob/living/silicon/robot/R in ai.connected_robots) - R.emagged = 0 - if (R.module) - if (R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - log_admin("[key_name_admin(usr)] has unemag'ed [ai]'s Cyborgs.") - - else if (href_list["common"]) - switch(href_list["common"]) - if("undress") - for(var/obj/item/W in current) - current.drop_from_inventory(W) - if("takeuplink") - take_uplink() - memory = null//Remove any memory they may have had. - if("crystals") - if (usr.client.holder.rights & R_FUN) - var/obj/item/device/uplink/hidden/suplink = find_syndicate_uplink() - var/crystals + else if (href_list["common"]) + switch(href_list["common"]) + if("undress") + for(var/obj/item/W in current) + current.drop_from_inventory(W) + if("takeuplink") + take_uplink() + memory = null//Remove any memory they may have had. + if("crystals") + if (usr.client.holder.rights & R_FUN) + var/obj/item/device/uplink/hidden/suplink = find_syndicate_uplink() + var/crystals + if (suplink) + crystals = suplink.uses + crystals = input("Amount of telecrystals for [key]","Operative uplink", crystals) as null|num + if (!isnull(crystals)) if (suplink) - crystals = suplink.uses - crystals = input("Amount of telecrystals for [key]","Operative uplink", crystals) as null|num - if (!isnull(crystals)) - if (suplink) - suplink.uses = crystals + suplink.uses = crystals - else if (href_list["obj_announce"]) - var/obj_count = 1 - current << "Your current objectives:" - for(var/datum/objective/objective in objectives) - current << "Objective #[obj_count]: [objective.explanation_text]" - obj_count++ - edit_memory() -/* - proc/clear_memory(var/silent = 1) - var/datum/game_mode/current_mode = ticker.mode + else if (href_list["obj_announce"]) + var/obj_count = 1 + current << "\blue Your current objectives:" + for(var/datum/objective/objective in objectives) + current << "Objective #[obj_count]: [objective.explanation_text]" + obj_count++ + edit_memory() - // remove traitor uplinks - var/list/L = current.get_contents() - for (var/t in L) - if (istype(t, /obj/item/device/pda)) - if (t:uplink) qdel(t:uplink) - t:uplink = null - else if (istype(t, /obj/item/device/radio)) - if (t:traitorradio) qdel(t:traitorradio) - t:traitorradio = null - t:traitor_frequency = 0.0 - else if (istype(t, /obj/item/weapon/SWF_uplink) || istype(t, /obj/item/weapon/syndicate_uplink)) - if (t:origradio) - var/obj/item/device/radio/R = t:origradio - R.loc = current.loc - R.traitorradio = null - R.traitor_frequency = 0.0 - qdel(t) +/datum/mind/proc/find_syndicate_uplink() + var/list/L = current.get_contents() + for (var/obj/item/I in L) + if (I.hidden_uplink) + return I.hidden_uplink + return null - // remove wizards spells - //If there are more special powers that need removal, they can be procced into here./N - current.spellremove(current) - - // clear memory - memory = "" - special_role = null - -*/ - - proc/find_syndicate_uplink() - var/list/L = current.get_contents() - for (var/obj/item/I in L) - if (I.hidden_uplink) - return I.hidden_uplink - return null - - proc/take_uplink() - var/obj/item/device/uplink/hidden/H = find_syndicate_uplink() - if(H) - qdel(H) +/datum/mind/proc/take_uplink() + var/obj/item/device/uplink/hidden/H = find_syndicate_uplink() + if(H) + qdel(H) - // check whether this mind's mob has been brigged for the given duration - // have to call this periodically for the duration to work properly - proc/is_brigged(duration) - var/turf/T = current.loc - if(!istype(T)) - brigged_since = -1 - return 0 - - var/is_currently_brigged = 0 - - if(istype(T.loc,/area/security/brig)) - is_currently_brigged = 1 - for(var/obj/item/weapon/card/id/card in current) +// check whether this mind's mob has been brigged for the given duration +// have to call this periodically for the duration to work properly +/datum/mind/proc/is_brigged(duration) + var/turf/T = current.loc + if(!istype(T)) + brigged_since = -1 + return 0 + var/is_currently_brigged = 0 + if(istype(T.loc,/area/security/brig)) + is_currently_brigged = 1 + for(var/obj/item/weapon/card/id/card in current) + is_currently_brigged = 0 + break // if they still have ID they're not brigged + for(var/obj/item/device/pda/P in current) + if(P.id) is_currently_brigged = 0 break // if they still have ID they're not brigged - for(var/obj/item/device/pda/P in current) - if(P.id) - is_currently_brigged = 0 - break // if they still have ID they're not brigged - if(!is_currently_brigged) - brigged_since = -1 - return 0 + if(!is_currently_brigged) + brigged_since = -1 + return 0 - if(brigged_since == -1) - brigged_since = world.time + if(brigged_since == -1) + brigged_since = world.time - return (duration <= world.time - brigged_since) + return (duration <= world.time - brigged_since) +/datum/mind/proc/reset() + assigned_role = null + special_role = null + role_alt_title = null + assigned_job = null + //faction = null //Uncommenting this causes a compile error due to 'undefined type', fucked if I know. + changeling = null + initial_account = null + objectives = list() + special_verbs = list() + has_been_rev = 0 + rev_cooldown = 0 + brigged_since = -1 //Antagonist role check /mob/living/proc/check_special_role(role) diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index d8c776bc36..8f048db043 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -130,11 +130,11 @@ /mob/living/silicon/ai/proc/ai_camera_track(var/target_name in trackable_mobs()) set category = "AI Commands" - set name = "Track With Camera" + set name = "Follow With Camera" set desc = "Select who you would like to track." if(src.stat == 2) - src << "You can't track with camera because you are dead!" + src << "You can't follow [target_name] with cameras because you are dead!" return if(!target_name) src.cameraFollow = null diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 3ea9b5157f..56b7b241c1 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -393,9 +393,9 @@ occupant.ckey = null //Make an announcement and log the person entering storage. - control_computer.frozen_crew += "[occupant.real_name], [occupant.mind.assigned_role] - [worldtime2text()]" + control_computer.frozen_crew += "[occupant.real_name], [occupant.mind.role_alt_title] - [worldtime2text()]" - announce.autosay("[occupant.real_name], [occupant.mind.assigned_role] [on_store_message]", "[on_store_name]") + announce.autosay("[occupant.real_name], [occupant.mind.role_alt_title], [on_store_message]", "[on_store_name]") visible_message("\The [initial(name)] hums and hisses as it moves [occupant.real_name] into storage.", 3) // Delete the mob. diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 0570211526..55cf5cdc15 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -123,6 +123,15 @@ Class Procs: /obj/machinery/Destroy() machines -= src + if(component_parts) + for(var/atom/A in component_parts) + if(A.loc == src) // If the components are inside the machine, delete them. + qdel(A) + else // Otherwise we assume they were dropped to the ground during deconstruction, and were not removed from the component_parts list by deconstruction code. + component_parts -= A + if(contents) // The same for contents. + for(var/atom/A in contents) + qdel(A) ..() /obj/machinery/process()//If you dont use process or power why are you here diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index d302738176..fbdb7667a1 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -1058,6 +1058,21 @@ var/global/list/obj/item/device/pda/PDAs = list() log_pda("[usr] (PDA: [sending_unit]) sent \"[message]\" to [name]") new_message = 1 +/obj/item/device/pda/verb/verb_reset_pda() + set category = "Object" + set name = "Reset PDA" + set src in usr + + if(issilicon(usr)) + return + + if(can_use(usr)) + mode = 0 + nanomanager.update_uis(src) + usr << "You press the reset button on \the [src]." + else + usr << "You cannot do this while restrained." + /obj/item/device/pda/verb/verb_remove_id() set category = "Object" set name = "Remove id" diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm index d51a9a1e3b..7c21c9e9f5 100644 --- a/code/game/objects/items/weapons/tools.dm +++ b/code/game/objects/items/weapons/tools.dm @@ -262,6 +262,8 @@ //Removes fuel from the welding tool. If a mob is passed, it will perform an eyecheck on the mob. This should probably be renamed to use() /obj/item/weapon/weldingtool/proc/remove_fuel(var/amount = 1, var/mob/M = null) + if(!welding) + return 0 if(get_fuel() >= amount) reagents.remove_reagent("fuel", amount) if(M) @@ -292,7 +294,7 @@ var/turf/T = get_turf(src) //If we're turning it on if(set_welding && !welding) - if (remove_fuel(1)) + if (get_fuel() > 0) if(M) M << "You switch the [src] on." else if(T) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 7d2ca17eb9..68aeecd912 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -220,8 +220,11 @@ if(istype(W, /obj/item/weapon/weldingtool)) var/obj/item/weapon/weldingtool/WT = W if(!WT.remove_fuel(0,user)) - user << "You need more welding fuel to complete this task." - return + if(!WT.isOn()) + return + else + user << "You need more welding fuel to complete this task." + return new /obj/item/stack/material/steel(src.loc) for(var/mob/M in viewers(src)) M.show_message("\The [src] has been cut apart by [user] with \the [WT].", 3, "You hear welding.", 2) @@ -239,8 +242,11 @@ else if(istype(W, /obj/item/weapon/weldingtool)) var/obj/item/weapon/weldingtool/WT = W if(!WT.remove_fuel(0,user)) - user << "You need more welding fuel to complete this task." - return + if(!WT.isOn()) + return + else + user << "You need more welding fuel to complete this task." + return src.welded = !src.welded src.update_icon() for(var/mob/M in viewers(src)) diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index cc154918e6..008d86d8ad 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -111,9 +111,12 @@ "You have [anchored ? "fastened the grille to" : "unfastened the grill from"] the floor.") return -//window placing begin - else if(istype(W,/obj/item/stack/material/glass)) - var/obj/item/stack/material/glass/ST = W +//window placing begin //TODO CONVERT PROPERLY TO MATERIAL DATUM + else if(istype(W,/obj/item/stack/material)) + var/obj/item/stack/material/ST = W + if(!ST.material.created_window) + return 0 + var/dir_to_set = 1 if(loc == user.loc) dir_to_set = user.dir @@ -143,7 +146,7 @@ user << "There is already a window facing this way there." return - var/wtype = ST.created_window + var/wtype = ST.material.created_window if (ST.use(1)) var/obj/structure/window/WD = new wtype(loc, dir_to_set, 1) user << "You place the [WD] on [src]." diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index ef4fa997c8..b415a317fc 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -177,6 +177,10 @@ var/list/admin_verbs_debug = list( /client/proc/toggledebuglogs, /client/proc/SDQL_query, /client/proc/SDQL2_query, + /client/proc/Jump, + /client/proc/jumptomob, + /client/proc/jumptocoord, + /client/proc/dsay ) var/list/admin_verbs_paranoid_debug = list( diff --git a/code/modules/admin/player_notes.dm b/code/modules/admin/player_notes.dm index a00a208d3b..c5a54694c8 100644 --- a/code/modules/admin/player_notes.dm +++ b/code/modules/admin/player_notes.dm @@ -27,26 +27,6 @@ datum/admins/proc/notes_gethtml(var/ckey) . += "[dir]
" return - -//handles adding notes to the end of a ckey's buffer -//originally had seperate entries such as var/by to record who left the note and when -//but the current bansystem is a heap of dung. -/proc/notes_add(var/ckey, var/note) - if(!ckey) - ckey = ckey(input(usr,"Who would you like to add notes for?","Enter a ckey",null) as text|null) - if(!ckey) return - - if(!note) - note = html_encode(input(usr,"Enter your note:","Enter some text",null) as message|null) - if(!note) return - - var/savefile/notesfile = new(NOTESFILE) - if(!notesfile) return - notesfile.cd = "/[ckey]" - notesfile.eof = 1 //move to the end of the buffer - notesfile << "[time2text(world.realtime,"DD-MMM-YYYY")] | [note][(usr && usr.ckey)?" ~[usr.ckey]":""]" - return - //handles removing entries from the buffer, or removing the entire directory if no start_index is given /proc/notes_remove(var/ckey, var/start_index, var/end_index) var/savefile/notesfile = new(NOTESFILE) @@ -85,7 +65,7 @@ datum/admins/proc/notes_gethtml(var/ckey) //Hijacking this file for BS12 playernotes functions. I like this ^ one systemm alright, but converting sounds too bothersome~ Chinsky. -/proc/notes_add(var/key, var/note, var/mob/usr) +/proc/notes_add(var/key, var/note, var/mob/user) if (!key || !note) return @@ -111,9 +91,9 @@ datum/admins/proc/notes_gethtml(var/ckey) var/day_loc = findtext(full_date, time2text(world.timeofday, "DD")) var/datum/player_info/P = new - if (usr) - P.author = usr.key - P.rank = usr.client.holder.rank + if (user) + P.author = user.key + P.rank = user.client.holder.rank else P.author = "Adminbot" P.rank = "Friendly Robot" @@ -123,8 +103,8 @@ datum/admins/proc/notes_gethtml(var/ckey) infos += P info << infos - message_admins("\blue [key_name_admin(usr)] has edited [key]'s notes.") - log_admin("[key_name(usr)] has edited [key]'s notes.") + message_admins("\blue [key_name_admin(user)] has edited [key]'s notes.") + log_admin("[key_name(user)] has edited [key]'s notes.") qdel(info) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 65d466164b..38dfc463b3 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -85,6 +85,7 @@ banreason = "[banreason] (CUSTOM CID)" else message_admins("Ban process: A mob matching [playermob.ckey] was found at location [playermob.x], [playermob.y], [playermob.z]. Custom ip and computer id fields replaced with the ip and computer id from the located mob") + notes_add(playermob.ckey,banreason,usr) DB_ban_record(bantype, playermob, banduration, banreason, banjob, null, banckey, banip, bancid ) @@ -671,7 +672,7 @@ msg = job else msg += ", [job]" - notes_add(M.ckey, "Banned from [msg] - [reason]") + notes_add(M.ckey, "Banned from [msg] - [reason]", usr) message_admins("\blue [key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes", 1) M << "\redYou have been jobbanned by [usr.client.ckey] from: [msg]." M << "\red The reason is: [reason]" @@ -692,7 +693,7 @@ jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") if(!msg) msg = job else msg += ", [job]" - notes_add(M.ckey, "Banned from [msg] - [reason]") + notes_add(M.ckey, "Banned from [msg] - [reason]", usr) message_admins("\blue [key_name_admin(usr)] banned [key_name_admin(M)] from [msg]", 1) M << "\redYou have been jobbanned by [usr.client.ckey] from: [msg]." M << "\red The reason is: [reason]" @@ -746,25 +747,7 @@ message_admins("\blue [key_name_admin(usr)] booted [key_name_admin(M)].", 1) //M.client = null qdel(M.client) -/* - //Player Notes - else if(href_list["notes"]) - var/ckey = href_list["ckey"] - if(!ckey) - var/mob/M = locate(href_list["mob"]) - if(ismob(M)) - ckey = M.ckey - switch(href_list["notes"]) - if("show") - notes_show(ckey) - if("add") - notes_add(ckey,href_list["text"]) - notes_show(ckey) - if("remove") - notes_remove(ckey,text2num(href_list["from"]),text2num(href_list["to"])) - notes_show(ckey) -*/ else if(href_list["removejobban"]) if(!check_rights(R_BAN)) return @@ -799,6 +782,7 @@ return AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.") + notes_add(M.ckey,"[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.",usr) M << "\redYou have been banned by [usr.client.ckey].\nReason: [reason]." M << "\red This is a temporary ban, it will be removed in [mins] minutes." feedback_inc("ban_tmp",1) @@ -831,6 +815,7 @@ else M << "\red No ban appeals URL has been set." ban_unban_log_save("[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This is a permanent ban.") + notes_add(M.ckey,"[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This is a permanent ban.",usr) log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") message_admins("\blue[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") feedback_inc("ban_perma",1) diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index e84543e88f..e579ca1544 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -8,8 +8,7 @@ set name = "Jump to Area" set desc = "Area to jump to" set category = "Admin" - if(!check_rights(R_ADMIN)) - usr << "Only administrators may use this command." + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) return if(config.allow_admin_jump) @@ -25,8 +24,7 @@ /client/proc/jumptoturf(var/turf/T in world) set name = "Jump to Turf" set category = "Admin" - if(!check_rights(R_ADMIN)) - usr << "Only administrators may use this command." + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) return if(config.allow_admin_jump) log_admin("[key_name(usr)] jumped to [T.x],[T.y],[T.z] in [T.loc]") @@ -42,8 +40,7 @@ set category = "Admin" set name = "Jump to Mob" - if(!check_rights(R_ADMIN)) - usr << "Only administrators may use this command." + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) return if(config.allow_admin_jump) @@ -65,8 +62,7 @@ set category = "Admin" set name = "Jump to Coordinate" - if(!check_rights(R_ADMIN)) - usr << "Only administrators may use this command." + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) return if (config.allow_admin_jump) @@ -86,8 +82,7 @@ set category = "Admin" set name = "Jump to Key" - if(!check_rights(R_ADMIN)) - usr << "Only administrators may use this command." + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) return if(config.allow_admin_jump) @@ -111,8 +106,7 @@ set category = "Admin" set name = "Get Mob" set desc = "Mob to teleport" - if(!check_rights(R_ADMIN)) - usr << "Only administrators may use this command." + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) return if(config.allow_admin_jump) log_admin("[key_name(usr)] teleported [key_name(M)]") @@ -128,8 +122,7 @@ set name = "Get Key" set desc = "Key to teleport" - if(!check_rights(R_ADMIN)) - usr << "Only administrators may use this command." + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) return if(config.allow_admin_jump) @@ -155,8 +148,7 @@ /client/proc/sendmob(var/mob/M in sortmobs()) set category = "Admin" set name = "Send Mob" - if(!check_rights(R_ADMIN)) - usr << "Only administrators may use this command." + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) return var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas() if(A) diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index b3b6e3fdae..9739d0bed4 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -18,16 +18,7 @@ if (src.handle_spam_prevention(msg,MUTE_DEADCHAT)) return - var/stafftype = null - - if (src.holder.rights & R_MOD) - stafftype = "MOD" - - if (src.holder.rights & R_MENTOR) - stafftype = "MENTOR" - - if (src.holder.rights & R_ADMIN) - stafftype = "ADMIN" + var/stafftype = uppertext(holder.rank) msg = sanitize(msg) log_admin("[key_name(src)] : [msg]") diff --git a/code/modules/hydroponics/_hydro_setup.dm b/code/modules/hydroponics/_hydro_setup.dm index a3fd4604a6..4641aa3ace 100644 --- a/code/modules/hydroponics/_hydro_setup.dm +++ b/code/modules/hydroponics/_hydro_setup.dm @@ -55,4 +55,5 @@ #define TRAIT_PRODUCT_COLOUR 35 #define TRAIT_BIOLUM 36 #define TRAIT_BIOLUM_COLOUR 37 -#define TRAIT_IMMUTABLE 38 \ No newline at end of file +#define TRAIT_IMMUTABLE 38 +#define TRAIT_FLESH_COLOUR 39 \ No newline at end of file diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index c2a06af587..977059ab95 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -167,8 +167,8 @@ return /obj/item/weapon/reagent_containers/food/snacks/grown/throw_impact(atom/hit_atom) - ..() if(seed) seed.thrown_at(src,hit_atom) + ..() /obj/item/weapon/reagent_containers/food/snacks/grown/attackby(var/obj/item/weapon/W, var/mob/user) @@ -194,9 +194,11 @@ else if(seed.chems) if(istype(W,/obj/item/weapon/material/hatchet) && !isnull(seed.chems["woodpulp"])) user.show_message("You make planks out of \the [src]!", 1) + var/flesh_colour = seed.get_trait(TRAIT_FLESH_COLOUR) + if(!flesh_colour) flesh_colour = seed.get_trait(TRAIT_PRODUCT_COLOUR) for(var/i=0,i<2,i++) var/obj/item/stack/material/wood/NG = new (user.loc) - NG.color = seed.get_trait(TRAIT_PRODUCT_COLOUR) + if(flesh_colour) NG.color = flesh_colour for (var/obj/item/stack/material/wood/G in user.loc) if(G==NG) continue @@ -221,6 +223,15 @@ new /obj/item/weapon/reagent_containers/food/snacks/soydope(get_turf(src)) qdel(src) return + else if(seed.get_trait(TRAIT_FLESH_COLOUR)) + user << "You slice up \the [src]." + var/slices = rand(3,5) + var/reagents_to_transfer = round(reagents.total_volume/slices) + for(var/i=i;i<=slices;i++) + var/obj/item/weapon/reagent_containers/food/snacks/fruit_slice/F = new(get_turf(src),seed) + if(reagents_to_transfer) reagents.trans_to_obj(F,reagents_to_transfer) + qdel(src) + return ..() /obj/item/weapon/reagent_containers/food/snacks/grown/attack(var/mob/living/carbon/M, var/mob/user, var/def_zone) @@ -301,9 +312,11 @@ if(seed.kitchen_tag == "grass") user.show_message("You make a grass tile out of \the [src]!", 1) + var/flesh_colour = seed.get_trait(TRAIT_FLESH_COLOUR) + if(!flesh_colour) flesh_colour = seed.get_trait(TRAIT_PRODUCT_COLOUR) for(var/i=0,i<2,i++) var/obj/item/stack/tile/grass/G = new (user.loc) - G.color = seed.get_trait(TRAIT_PRODUCT_COLOUR) + if(flesh_colour) G.color = flesh_colour for (var/obj/item/stack/tile/grass/NG in user.loc) if(G==NG) continue @@ -358,3 +371,35 @@ /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosiavulgaris plantname = "ambrosia" + +/obj/item/weapon/reagent_containers/food/snacks/fruit_slice + name = "fruit slice" + desc = "A slice of some tasty fruit." + icon = 'icons/obj/hydroponics_misc.dmi' + icon_state = "" + +var/list/fruit_icon_cache = list() + +/obj/item/weapon/reagent_containers/food/snacks/fruit_slice/New(var/newloc, var/datum/seed/S) + ..(newloc) + // Need to go through and make a general image caching controller. Todo. + if(!istype(S)) + qdel(src) + return + + name = "[S.seed_name] slice" + desc = "A slice of \a [S.seed_name]. Tasty, probably." + + var/rind_colour = S.get_trait(TRAIT_PRODUCT_COLOUR) + var/flesh_colour = S.get_trait(TRAIT_FLESH_COLOUR) + if(!flesh_colour) flesh_colour = rind_colour + if(!fruit_icon_cache["rind-[rind_colour]"]) + var/image/I = image(icon,"fruit_rind") + I.color = rind_colour + fruit_icon_cache["rind-[rind_colour]"] = I + overlays |= fruit_icon_cache["rind-[rind_colour]"] + if(!fruit_icon_cache["slice-[rind_colour]"]) + var/image/I = image(icon,"fruit_slice") + I.color = flesh_colour + fruit_icon_cache["slice-[rind_colour]"] = I + overlays |= fruit_icon_cache["slice-[rind_colour]"] diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index 1cf969a090..d540d64b38 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -169,8 +169,9 @@ if(get_trait(TRAIT_BIOLUM_COLOUR)) clr = get_trait(TRAIT_BIOLUM_COLOUR) splat.set_light(get_trait(TRAIT_BIOLUM), l_color = clr) - if(get_trait(TRAIT_PRODUCT_COLOUR)) - splat.color = get_trait(TRAIT_PRODUCT_COLOUR) + var/flesh_colour = get_trait(TRAIT_FLESH_COLOUR) + if(!flesh_colour) flesh_colour = get_trait(TRAIT_PRODUCT_COLOUR) + if(flesh_colour) splat.color = get_trait(TRAIT_PRODUCT_COLOUR) if(chems) for(var/mob/living/M in T.contents) @@ -229,7 +230,8 @@ for(var/mob/living/M in T.contents) apply_special_effect(M) splatter(T,thrown) - origin_turf.visible_message("The [thrown.name] explodes!") + if(origin_turf) + origin_turf.visible_message("The [thrown.name] explodes!") qdel(thrown) return @@ -242,7 +244,8 @@ if(get_trait(TRAIT_JUICY) && splatted) splatter(origin_turf,thrown) - origin_turf.visible_message("The [thrown.name] splatters against [target]!") + if(origin_turf) + origin_turf.visible_message("The [thrown.name] splatters against [target]!") qdel(thrown) /datum/seed/proc/handle_environment(var/turf/current_turf, var/datum/gas_mixture/environment, var/light_supplied, var/check_only) @@ -637,7 +640,7 @@ if(GENE_STRUCTURE) traits_to_copy = list(TRAIT_PLANT_ICON,TRAIT_PRODUCT_ICON,TRAIT_HARVEST_REPEAT) if(GENE_FRUIT) - traits_to_copy = list(TRAIT_STINGS,TRAIT_EXPLOSIVE,TRAIT_JUICY) + traits_to_copy = list(TRAIT_STINGS,TRAIT_EXPLOSIVE,TRAIT_FLESH_COLOUR,TRAIT_JUICY) if(GENE_SPECIAL) traits_to_copy = list(TRAIT_TELEPORTING) diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm index cfbf024eda..b8b0530788 100644 --- a/code/modules/hydroponics/seed_datums.dm +++ b/code/modules/hydroponics/seed_datums.dm @@ -244,6 +244,7 @@ set_trait(TRAIT_PRODUCT_ICON,"apple") set_trait(TRAIT_PRODUCT_COLOUR,"#FF540A") set_trait(TRAIT_PLANT_ICON,"tree2") + set_trait(TRAIT_FLESH_COLOUR,"#E8E39B") /datum/seed/apple/poison name = "poisonapple" @@ -804,6 +805,7 @@ set_trait(TRAIT_PRODUCT_COLOUR,"#326B30") set_trait(TRAIT_PLANT_COLOUR,"#257522") set_trait(TRAIT_PLANT_ICON,"vine2") + set_trait(TRAIT_FLESH_COLOUR,"#F22C2C") /datum/seed/pumpkin name = "pumpkin" @@ -842,6 +844,7 @@ set_trait(TRAIT_PRODUCT_ICON,"treefruit") set_trait(TRAIT_PRODUCT_COLOUR,"#3AF026") set_trait(TRAIT_PLANT_ICON,"tree") + set_trait(TRAIT_FLESH_COLOUR,"#3AF026") /datum/seed/citrus/lemon name = "lemon" @@ -854,6 +857,7 @@ ..() set_trait(TRAIT_PRODUCES_POWER,1) set_trait(TRAIT_PRODUCT_COLOUR,"#F0E226") + set_trait(TRAIT_FLESH_COLOUR,"#F0E226") /datum/seed/citrus/orange name = "orange" @@ -865,6 +869,7 @@ /datum/seed/citrus/orange/New() ..() set_trait(TRAIT_PRODUCT_COLOUR,"#FFC20A") + set_trait(TRAIT_FLESH_COLOUR,"#FFC20A") /datum/seed/grass name = "grass" diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index e62a96c2ce..87894c6d73 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -196,16 +196,19 @@ layer = 3 density = 0 -/obj/effect/plant/proc/calc_dir(turf/location = loc) +/obj/effect/plant/proc/calc_dir() set background = 1 + var/turf/T = get_turf(src) + if(!istype(T)) return + var/direction = 16 for(var/wallDir in cardinal) - var/turf/newTurf = get_step(location,wallDir) + var/turf/newTurf = get_step(T,wallDir) if(newTurf.density) direction |= wallDir - for(var/obj/effect/plant/shroom in location) + for(var/obj/effect/plant/shroom in T.contents) if(shroom == src) continue if(shroom.floor) //special diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index c41b6fc449..82d55960df 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -47,6 +47,19 @@ return 0 ..(S,tamount,1) +/obj/item/stack/material/attack_self(var/mob/user) + if(!material.build_windows(user, src)) + ..() + +/obj/item/stack/material/attackby(var/obj/item/W, var/mob/user) + if(istype(W,/obj/item/stack/cable_coil)) + material.build_wired_product(user, W, src) + return + else if(istype(W, /obj/item/stack/rods)) + material.build_rod_product(user, W, src) + return + return ..() + /obj/item/stack/material/iron name = "iron" icon_state = "sheet-silver" @@ -75,7 +88,7 @@ default_type = "phoron" /obj/item/stack/material/plastic - name = "Plastic" + name = "plastic" icon_state = "sheet-plastic" default_type = "plastic" @@ -151,3 +164,27 @@ singular_name = "leather piece" icon_state = "sheet-leather" default_type = "leather" + +/obj/item/stack/material/glass + name = "glass" + singular_name = "glass sheet" + icon_state = "sheet-glass" + default_type = "glass" + +/obj/item/stack/material/glass/reinforced + name = "reinforced glass" + singular_name = "reinforced glass sheet" + icon_state = "sheet-rglass" + default_type = "reinforced glass" + +/obj/item/stack/material/glass/phoronglass + name = "phoron glass" + singular_name = "phoron glass sheet" + icon_state = "sheet-phoronglass" + default_type = "phoron glass" + +/obj/item/stack/material/glass/phoronrglass + name = "reinforced phoron glass" + singular_name = "reinforced phoron glass sheet" + icon_state = "sheet-phoronrglass" + default_type = "reinforced phoron glass" diff --git a/code/modules/materials/material_synth.dm b/code/modules/materials/material_synth.dm index a0977e69c5..23b0e7e546 100644 --- a/code/modules/materials/material_synth.dm +++ b/code/modules/materials/material_synth.dm @@ -13,20 +13,26 @@ matter = null /obj/item/stack/material/cyborg/plastic + icon_state = "sheet-plastic" default_type = "plastic" /obj/item/stack/material/cyborg/steel + icon_state = "sheet-metal" default_type = "steel" /obj/item/stack/material/cyborg/plasteel + icon_state = "sheet-plasteel" default_type = "plasteel" /obj/item/stack/material/cyborg/wood + icon_state = "sheet-wood" default_type = "wood" /obj/item/stack/material/cyborg/glass + icon_state = "sheet-glass" default_type = "glass" /obj/item/stack/material/cyborg/glass/reinforced + icon_state = "sheet-rglass" default_type = "reinforced glass" charge_costs = list(500, 1000) \ No newline at end of file diff --git a/code/modules/materials/materials.dm b/code/modules/materials/materials.dm index 7e0a8ef71a..802ac7ccc4 100644 --- a/code/modules/materials/materials.dm +++ b/code/modules/materials/materials.dm @@ -76,6 +76,12 @@ var/list/name_to_material var/conductive = 1 // Objects with this var add CONDUCTS to flags on spawn. var/list/composite_material // If set, object matter var will be a list containing these values. + // Placeholder vars for the time being, todo properly integrate windows/light tiles/rods. + var/created_window + var/rod_product + var/wire_product + var/list/window_options = list() + // Damage values. var/hardness = 60 // Prob of wall destruction by hulk, used for edge damage in weapons. var/weight = 20 // Determines blunt damage/throwforce for weapons. @@ -89,6 +95,37 @@ var/list/name_to_material // Wallrot crumble message. var/rotting_touch_message = "crumbles under your touch" +// Placeholders for light tiles and rglass. +/material/proc/build_rod_product(var/mob/user, var/obj/item/stack/used_stack, var/obj/item/stack/target_stack) + if(!rod_product) + user << "You cannot make anything out of \the [target_stack]" + return + if(used_stack.get_amount() < 1 || target_stack.get_amount() < 1) + user << "You need one rod and one sheet of [display_name] to make anything useful." + return + used_stack.use(1) + target_stack.use(1) + var/obj/item/stack/S = new rod_product(get_turf(user)) + S.add_fingerprint(user) + S.add_to_stacks(user) + if(!(user.l_hand && user.r_hand)) + user.put_in_hands(S) + +/material/proc/build_wired_product(var/mob/user, var/obj/item/stack/used_stack, var/obj/item/stack/target_stack) + if(!wire_product) + user << "You cannot make anything out of \the [target_stack]" + return + if(used_stack.get_amount() < 5 || target_stack.get_amount() < 1) + user << "You need five wires and one sheet of [display_name] to make anything useful." + return + + used_stack.use(5) + target_stack.use(1) + user << "You attach wire to the [name]." + var/obj/item/product = new wire_product(get_turf(user)) + if(!(user.l_hand && user.r_hand)) + user.put_in_hands(product) + // Make sure we have a display name and shard icon even if they aren't explicitly set. /material/New() ..() @@ -99,6 +136,10 @@ var/list/name_to_material if(!shard_icon) shard_icon = shard_type +// This is a placeholder for proper integration of windows/windoors into the system. +/material/proc/build_windows(var/mob/living/user, var/obj/item/stack/used_stack) + return 0 + // Weapons handle applying a divisor for this value locally. /material/proc/get_blunt_damage() return weight //todo @@ -271,7 +312,7 @@ var/list/name_to_material hardness = 80 weight = 23 stack_origin_tech = list(TECH_MATERIAL = 2) - composite_material = list() //todo + composite_material = list(DEFAULT_WALL_MATERIAL = 3750, "platinum" = 3750) //todo /material/glass name = "glass" @@ -286,21 +327,82 @@ var/list/name_to_material weight = 15 door_icon_base = "stone" destruction_desc = "shatters" + window_options = list("One Direction", "Full Window") + created_window = /obj/structure/window/basic + wire_product = /obj/item/stack/light_w + rod_product = /obj/item/stack/material/glass/reinforced -/material/glass/phoron - name = "phoron glass" - stack_type = /obj/item/stack/material/glass/phoronglass - flags = MATERIAL_BRITTLE - ignition_point = 300 - integrity = 200 // idk why but phoron windows are strong, so. - icon_colour = "#FC2BC5" - stack_origin_tech = list(TECH_MATERIAL = 3, TECH_PHORON = 2) +/material/glass/build_windows(var/mob/living/user, var/obj/item/stack/used_stack) -/material/glass/phoron/reinforced - name = "reinforced phoron glass" - stack_type = /obj/item/stack/material/glass/phoronrglass - stack_origin_tech = list(TECH_MATERIAL = 4, TECH_PHORON = 2) - composite_material = list() //todo + if(!user || !used_stack || !created_window || !window_options.len) + return 0 + + if(!user.IsAdvancedToolUser()) + user << "This task is too complex for your clumsy hands." + return 1 + + var/turf/T = user.loc + if(!istype(T)) + user << "You must be standing on open flooring to build a window." + return 1 + + var/title = "Sheet-[used_stack.name] ([used_stack.get_amount()] sheet\s left)" + var/choice = input(title, "What would you like to construct?") as null|anything in window_options + + if(!choice || !used_stack || !user || used_stack.loc != user || user.stat || user.loc != T) + return 1 + + // Get data for building windows here. + var/list/possible_directions = cardinal.Copy() + var/window_count = 0 + for (var/obj/structure/window/check_window in user.loc) + window_count++ + possible_directions -= check_window.dir + + // Get the closest available dir to the user's current facing. + var/build_dir = SOUTHWEST //Default to southwest for fulltile windows. + var/failed_to_build + + if(window_count >= 4) + failed_to_build = 1 + else + if(choice in list("One Direction","Windoor")) + if(possible_directions.len) + for(var/direction in list(user.dir, turn(user.dir,90), turn(user.dir,180), turn(user.dir,270) )) + if(direction in possible_directions) + build_dir = direction + break + else + failed_to_build = 1 + if(!failed_to_build && choice == "Windoor") + if(!is_reinforced()) + user << "This material is not reinforced enough to use for a door." + return + if((locate(/obj/structure/windoor_assembly) in T.contents) || (locate(/obj/machinery/door/window) in T.contents)) + failed_to_build = 1 + if(failed_to_build) + user << "There is no room in this location." + return 1 + + var/build_path = /obj/structure/windoor_assembly + var/sheets_needed = 4 + if(choice == "Windoor") + sheets_needed = 5 + build_dir = user.dir + else + build_path = created_window + + if(used_stack.get_amount() < sheets_needed) + user << "You need at least [sheets_needed] sheets to build this." + return 1 + + // Build the structure and update sheet count etc. + used_stack.use(sheets_needed) + new build_path(T, build_dir, 1) + return 1 + +/material/glass/proc/is_reinforced() + return (hardness > 35) //todo /material/glass/reinforced name = "reinforced glass" @@ -313,8 +415,36 @@ var/list/name_to_material tableslam_noise = 'sound/effects/Glasshit.ogg' hardness = 40 weight = 30 + stack_origin_tech = "materials=2" + composite_material = list(DEFAULT_WALL_MATERIAL = 1875,"glass" = 3750) + window_options = list("One Direction", "Full Window", "Windoor") + created_window = /obj/structure/window/reinforced + wire_product = null + rod_product = null + +/material/glass/phoron + name = "phoron glass" + stack_type = /obj/item/stack/material/glass/phoronglass + flags = MATERIAL_BRITTLE + ignition_point = 300 + integrity = 200 // idk why but phoron windows are strong, so. + icon_colour = "#FC2BC5" + stack_origin_tech = list(TECH_MATERIAL = 3, TECH_PHORON = 2) + created_window = /obj/structure/window/phoronbasic + wire_product = null + rod_product = /obj/item/stack/material/glass/phoronrglass + +/material/glass/phoron/reinforced + name = "reinforced phoron glass" + stack_type = /obj/item/stack/material/glass/phoronrglass + stack_origin_tech = list(TECH_MATERIAL = 4, TECH_PHORON = 2) + composite_material = list() //todo + created_window = /obj/structure/window/phoronreinforced + hardness = 40 + weight = 30 stack_origin_tech = list(TECH_MATERIAL = 2) composite_material = list() //todo + rod_product = null /material/plastic name = "plastic" diff --git a/code/modules/mob/freelook/mask/update_triggers.dm b/code/modules/mob/freelook/mask/update_triggers.dm index a8e68058fd..8100a309a2 100644 --- a/code/modules/mob/freelook/mask/update_triggers.dm +++ b/code/modules/mob/freelook/mask/update_triggers.dm @@ -35,7 +35,7 @@ cultnet.updateVisibility(src, 0) /mob/living/death(gibbed, deathmessage="seizes up and falls limp...") - if(..()) + if(..(gibbed, deathmessage)) // If true, the mob went from living to dead (assuming everyone has been overriding as they should...) cultnet.updateVisibility(src) diff --git a/code/modules/mob/living/carbon/brain/posibrain.dm b/code/modules/mob/living/carbon/brain/posibrain.dm index 05da370fc8..9e3a127136 100644 --- a/code/modules/mob/living/carbon/brain/posibrain.dm +++ b/code/modules/mob/living/carbon/brain/posibrain.dm @@ -53,6 +53,29 @@ if(!searching || (src.brainmob && src.brainmob.key)) return searching = 0 +/obj/item/device/mmi/digital/posibrain/proc/transfer_personality(var/mob/candidate) + announce_ghost_joinleave(candidate, 0, "They are occupying a positronic brain now.") + src.searching = 0 + src.brainmob.mind = candidate.mind + src.brainmob.ckey = candidate.ckey + src.brainmob.mind.reset() + src.name = "positronic brain ([src.brainmob.name])" + src.brainmob << "You are a positronic brain, brought into existence on [station_name()]." + src.brainmob << "As a synthetic intelligence, you answer to all crewmembers, as well as the AI." + src.brainmob << "Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm." + src.brainmob << "Use say :b to speak to other artificial intelligences." + src.brainmob.mind.assigned_role = "Positronic Brain" + + var/turf/T = get_turf_or_move(src.loc) + for (var/mob/M in viewers(T)) + M.show_message("\blue The positronic brain chimes quietly.") + icon_state = "posibrain-occupied" + +/obj/item/device/mmi/digital/posibrain/proc/reset_search() //We give the players sixty seconds to decide, then reset the timer. + + if(src.brainmob && src.brainmob.key) return + + src.searching = 0 icon_state = "posibrain" var/turf/T = get_turf(src) T.visible_message("\The [src] brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?") diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index b0a27ad52f..9d54b25198 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -186,6 +186,19 @@ src.reset_view(C) return 1 +/mob/living/silicon/pai/verb/reset_record_view() + set category = "pAI Commands" + set name = "Reset Records Software" + + securityActive1 = null + securityActive2 = null + security_cannotfind = 0 + medicalActive1 = null + medicalActive2 = null + medical_cannotfind = 0 + nanomanager.update_uis(src) + usr << "You reset your record-viewing software." + /mob/living/silicon/pai/cancel_camera() set category = "pAI Commands" set name = "Cancel Camera View" diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index e2ec5059a2..4b33818033 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -295,7 +295,7 @@ if(!O.force) visible_message("[user] gently taps [src] with \the [O].") return - + if(O.force > resistance) var/damage = O.force if (O.damtype == HALLOSS) @@ -306,8 +306,8 @@ adjustBruteLoss(damage) else usr << "[src] has been attacked with the [O] by [user].") + + visible_message("\The [src] has been attacked with the [O] by [user].") user.do_attack_animation(src) /mob/living/simple_animal/movement_delay() @@ -327,10 +327,10 @@ if(statpanel("Status") && show_stat_health) stat(null, "Health: [round((health / maxHealth) * 100)]%") -/mob/living/simple_animal/death(gibbed, deathmessage="") +/mob/living/simple_animal/death(gibbed, deathmessage = "dies!") icon_state = icon_dead density = 0 - return ..() + return ..(gibbed,deathmessage) /mob/living/simple_animal/ex_act(severity) if(!blinded) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 2264f60a54..83ae948ef3 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -376,7 +376,8 @@ return M.key = key -// M.Login() //wat + if(M.mind) + M.mind.reset() return /client/verb/changes() diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index e6add5089c..e778c42fcf 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -26,6 +26,27 @@ var/list/organ_cache = list() var/datum/dna/dna var/datum/species/species +/obj/item/organ/Destroy() + if(!owner) + return ..() + + if((owner.internal_organs) && (src in owner.internal_organs)) + owner.internal_organs -= src + + if((owner.internal_organs_by_name) && (src in owner.internal_organs_by_name)) + owner.internal_organs_by_name -= src + + if((owner.organs) && (src in owner.organs)) + owner.organs -= src + + if((owner.organs_by_name) && (src in owner.organs_by_name)) + owner.organs_by_name -= src + + if(src in owner.contents) + owner.contents -= src + + return ..() + /obj/item/organ/proc/update_health() return diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index cd44a09eeb..a4ab332221 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -51,6 +51,20 @@ var/can_grasp var/can_stand +/obj/item/organ/external/Destroy() + if(parent && parent.children) + parent.children -= src + + if(children) + for(var/obj/item/organ/external/C in children) + qdel(C) + + if(internal_organs) + for(var/obj/item/organ/O in internal_organs) + qdel(O) + + return ..() + /obj/item/organ/external/attack_self(var/mob/user) if(!contents.len) return ..() diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm index 2faa50a4e6..5930dd53e7 100644 --- a/code/modules/power/breaker_box.dm +++ b/code/modules/power/breaker_box.dm @@ -19,6 +19,11 @@ var/RCon_tag = "NO_TAG" var/update_locked = 0 +/obj/machinery/power/breakerbox/Destroy() + ..() + for(var/obj/nano_module/rcon/R in world) + R.FindDevices() + /obj/machinery/power/breakerbox/activated icon_state = "bbox_on" diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 3b9d094bda..7d51af8e35 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -17,6 +17,8 @@ /obj/machinery/power/Destroy() disconnect_from_network() + disconnect_terminal() + ..() /////////////////////////////// diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 6c64730eb6..5ce02cf18d 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -84,10 +84,12 @@ return -/obj/machinery/power/smes/Destroy() +/obj/machinery/power/smes/disconnect_terminal() if(terminal) - disconnect_terminal() - ..() + terminal.master = null + terminal = null + return 1 + return 0 /obj/machinery/power/smes/update_icon() overlays.Cut() diff --git a/code/modules/power/smes_construction.dm b/code/modules/power/smes_construction.dm index 94e628bad9..36924d9ae7 100644 --- a/code/modules/power/smes_construction.dm +++ b/code/modules/power/smes_construction.dm @@ -75,6 +75,13 @@ charge = 0 should_be_mapped = 1 +/obj/machinery/power/smes/buildable/Destroy() + ..() + qdel(wires) + for(var/obj/nano_module/rcon/R in world) + R.FindDevices() + + // Proc: process() // Parameters: None // Description: Uses parent process, but if grounding wire is cut causes sparks to fly around. @@ -343,6 +350,7 @@ M.icon_state = "box_1" for(var/obj/I in component_parts) I.loc = src.loc + component_parts -= I qdel(src) return diff --git a/icons/mob/species/unathi/tail.dmi b/icons/mob/species/unathi/tail.dmi index 97ae78393a..eba37dba6a 100644 Binary files a/icons/mob/species/unathi/tail.dmi and b/icons/mob/species/unathi/tail.dmi differ diff --git a/icons/obj/hydroponics_misc.dmi b/icons/obj/hydroponics_misc.dmi new file mode 100644 index 0000000000..a5c594d93f Binary files /dev/null and b/icons/obj/hydroponics_misc.dmi differ diff --git a/icons/turf/wall_masks.dmi b/icons/turf/wall_masks.dmi index c7fd0f0e81..0094e605b8 100644 Binary files a/icons/turf/wall_masks.dmi and b/icons/turf/wall_masks.dmi differ diff --git a/tools/dmitool/dmitool.jar b/tools/dmitool/dmitool.jar index 34bdb5bdd4..c7df4d8b7f 100644 Binary files a/tools/dmitool/dmitool.jar and b/tools/dmitool/dmitool.jar differ diff --git a/tools/dmitool/dmitool.py b/tools/dmitool/dmitool.py new file mode 100644 index 0000000000..390f0d745f --- /dev/null +++ b/tools/dmitool/dmitool.py @@ -0,0 +1,94 @@ +""" Python 2.7 wrapper for dmitool. +""" + +import os +from subprocess import Popen, PIPE + +_JAVA_PATH = ["java"] +_DMITOOL_CMD = ["-jar", "dmitool.jar"] + +def _dmitool_call(*dmitool_args, **popen_args): + return Popen(_JAVA_PATH + _DMITOOL_CMD + [str(arg) for arg in dmitool_args], **popen_args) + +def _safe_parse(dict, key, deferred_value): + try: + dict[key] = deferred_value() + except Exception as e: + print "Could not parse property '%s': %s"%(key, e) + return e + return False + +def version(): + """ Returns the version as a string. """ + stdout, stderr = _dmitool_call("version", stdout=PIPE).communicate() + return str(stdout).strip() + +def help(): + """ Returns the help text as a string. """ + stdout, stderr = _dmitool_call("help", stdout=PIPE).communicate() + return str(stdout).strip() + +def info(filepath): + """ Totally not a hack that parses the output from dmitool into a dictionary. + May break at any moment. + """ + subproc = _dmitool_call("info", filepath, stdout=PIPE) + stdout, stderr = subproc.communicate() + + result = {} + data = stdout.split(os.linesep)[1:] + #for s in data: print s + + #parse header line + if len(data) > 0: + header = data.pop(0).split(",") + #don't need to parse states, it's redundant + _safe_parse(result, "images", lambda: int(header[0].split()[0].strip())) + _safe_parse(result, "size", lambda: header[2].split()[1].strip()) + + #parse state information + states = [] + for item in data: + if not len(item): continue + + stateinfo = {} + item = item.split(",", 3) + _safe_parse(stateinfo, "name", lambda: item[0].split()[1].strip(" \"")) + _safe_parse(stateinfo, "dirs", lambda: int(item[1].split()[0].strip())) + _safe_parse(stateinfo, "frames", lambda: int(item[2].split()[0].strip())) + if len(item) > 3: + stateinfo["misc"] = item[3] + + states.append(stateinfo) + + result["states"] = states + return result + +def extract_state(input_path, output_path, icon_state, direction=None, frame=None): + """ Extracts an icon state as a png to a given path. + If provided direction should be a string, one of S, N, E, W, SE, SW, NE, NW. + If provided frame should be a frame number or a string of two frame number separated by a dash. + """ + args = ["extract", input_path, icon_state, output_path] + if direction is not None: args.extend(("direction" , str(direction))) + if frame is not None: args.extend(("frame" , str(frame))) + return _dmitool_call(*args) + +def import_state(target_path, input_path, icon_state, replace=False, delays=None, rewind=False, loop=None, ismovement=False, direction=None, frame=None): + """ Inserts an input png given by the input_path into the target_path. + """ + args = ["import", target_path, icon_state, input_path] + + if replace: args.append("nodup") + if rewind: args.append("rewind") + if ismovement: args.append("movement") + if delays: args.extend(("delays", ",".join(delays))) + if direction is not None: args.extend(("direction", direction)) + if frame is not None: args.extend(("frame", frame)) + + if loop in ("inf", "infinity"): + args.append("loop") + elif loop: + args.extend(("loopn", loop)) + + return _dmitool_call(*args) diff --git a/tools/dmitool/src/main/java/dmitool/IconState.java b/tools/dmitool/src/main/java/dmitool/IconState.java index c3eb2cff21..2a2202c71c 100644 --- a/tools/dmitool/src/main/java/dmitool/IconState.java +++ b/tools/dmitool/src/main/java/dmitool/IconState.java @@ -175,7 +175,7 @@ public class IconState { } int pxW = in.imgInfo.cols; int pxH = in.imgInfo.rows; - int frames = pxW / w; + int frames = pxW / w; //frames are read along the X axis, dirs along the Y, much like export. int dirs = pxH / h; // make sure the size is an integer multiple @@ -203,7 +203,7 @@ public class IconState { px[bY][bX + 3]); } } - images[imageY + imageX*dirs] = new NonPalettedImage(w, h, pixels); + images[_getIndex(imageY, imageX, dirs)] = new NonPalettedImage(w, h, pixels); } } @@ -211,6 +211,38 @@ public class IconState { return new IconState(name, dirs, frames, images, delays, rewind, loop, hotspot, movement); } + + //Converts a desired dir and frame to an index into the images array. + public int getIndex(int dir, int frame) { + return _getIndex(dir, frame, dirs); + } + + private static int _getIndex(int dir, int frame, int totalDirs) { + return dir + frame*totalDirs; + } + + public void insertDir(int dir, Image[] splice) { + int maxFrame = frames < splice.length? frames: splice.length; + for(int frameIdx = 0; frameIdx < maxFrame; frameIdx++) { + insertImage(dir, frameIdx, splice[frameIdx]); + } + } + + public void insertFrame(int frame, Image[] splice) { + int maxDir = dirs < splice.length? dirs: splice.length; + for(int dirIdx = 0; dirIdx < maxDir; dirIdx++) { + insertImage(dirIdx, frame, splice[dirIdx]); + } + } + + public void insertImage(int dir, int frame, Image splice) { + if(frame < 0 || frame >= frames) + throw new IllegalArgumentException("Provided frame is out of range: " + frame); + if(dir < 0 || dir >= dirs) + throw new IllegalArgumentException("Provided dir is out of range: " + dir); + + images[getIndex(dir, frame)] = splice; + } } @@ -244,6 +276,5 @@ public class IconState { - diff --git a/tools/dmitool/src/main/java/dmitool/Main.java b/tools/dmitool/src/main/java/dmitool/Main.java index 493d7b58fe..fe6048ca3b 100644 --- a/tools/dmitool/src/main/java/dmitool/Main.java +++ b/tools/dmitool/src/main/java/dmitool/Main.java @@ -59,6 +59,7 @@ public class Main { "\t movement | move | mov | m : [state] should be marked as a movement state\n" + "\t delays L | delay L | del L | d L : use the list L as a comma-separated list of delays (e.g. '1,1,2,2,1')\n" + "\t hotspot H | hs H | h H : use H as the hotspot for this state\n" + + "\t direction D | dir D : replaces D with the image from [in], instead of the entire state. D can be 0-7 or S, N, E, etc. If the state does not already exist, this is ignored\n" + ""; public static void main(String[] args) throws FileNotFoundException, IOException, DMIException { @@ -270,6 +271,8 @@ public class Main { boolean movement = false; String hotspot = null; float[] delays = null; + String replaceDir = null; + String replaceFrame = null; while(!argq.isEmpty()) { String s = argq.pollFirst(); switch(s.toLowerCase()) { @@ -341,6 +344,24 @@ public class Main { return; } break; + case "dir": + case "direction": + if(!argq.isEmpty()) { + replaceDir = argq.pollFirst(); + } else { + System.out.println("Argument '" + s + "' requires a direction argument following it!"); + return; + } + break; + case "f": + case "frame": + if(!argq.isEmpty()) { + replaceFrame = argq.pollFirst(); + } else { + System.out.println("Argument '" + s + "' requires a frame argument following it!"); + return; + } + break; default: System.out.println("Unknown import argument '" + s + "', ignoring."); break; @@ -352,12 +373,46 @@ public class Main { if(VERBOSITY >= 0) toImportTo.printInfo(); IconState is = IconState.importFromPNG(toImportTo, new FileInputStream(pngFile), stateName, delays, rewind, loop, hotspot, movement); - if(noDup) { - if(!toImportTo.setIconState(is)) { + //image insertion + if(replaceDir != null || replaceFrame != null) { + + IconState targetIs = toImportTo.getIconState(stateName); + if(targetIs == null) { + System.out.println("'direction' or 'frame' specified and no icon state '" + stateName + "' found, aborting!"); + return; + } + if(is.images.length == 0) { + System.out.println("'direction' or 'frame' specified and imported is empty, aborting!"); + return; + } + + if(!noDup) targetIs = targetIs.clone(); + + int dirToReplace, frameToReplace; + if(replaceDir != null && replaceFrame != null) { + frameToReplace = parseFrame(replaceFrame, targetIs); + dirToReplace = parseDir(replaceDir, targetIs); + targetIs.insertImage(dirToReplace, frameToReplace, is.images[0]); + } + else if(replaceDir != null) { + dirToReplace = parseDir(replaceDir, targetIs); + targetIs.insertDir(dirToReplace, is.images); + } + else if(replaceFrame != null) { + frameToReplace = parseFrame(replaceFrame, targetIs); + targetIs.insertFrame(frameToReplace, is.images); + } + + if(!noDup) toImportTo.addIconState(null, targetIs); + } + else { + if(noDup) { + if(!toImportTo.setIconState(is)) { + toImportTo.addIconState(null, is); + } + } else { toImportTo.addIconState(null, is); } - } else { - toImportTo.addIconState(null, is); } if(VERBOSITY >= 0) toImportTo.printInfo();