-
Notifications
You must be signed in to change notification settings - Fork 4.9k
/
limbgrower.dm
276 lines (236 loc) · 10.5 KB
/
limbgrower.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/// The limbgrower. Makes organd and limbs with synthflesh and chems.
/// See [limbgrower_designs.dm] for everything we can make.
/obj/machinery/limbgrower
name = "limb grower"
desc = "It grows new limbs using Synthflesh."
icon = 'icons/obj/machines/limbgrower.dmi'
icon_state = "limbgrower_idleoff"
density = TRUE
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 100
circuit = /obj/item/circuitboard/machine/limbgrower
/// The category of limbs we're browing in our UI.
var/selected_category = SPECIES_HUMAN
/// If we're currently printing something.
var/busy = FALSE
/// How efficient our machine is. Better parts = less chemicals used and less power used. Range of 1 to 0.25.
var/production_coefficient = 1
/// How long it takes for us to print a limb. Affected by production_coefficient.
var/production_speed = 3 SECONDS
/// The design we're printing currently.
var/datum/design/being_built
/// Our internal techweb for limbgrower designs.
var/datum/techweb/stored_research
/// All the categories of organs we can print.
var/list/categories = list(SPECIES_HUMAN, SPECIES_LIZARD, SPECIES_MOTH, SPECIES_PLASMAMAN, SPECIES_ETHEREAL, "other")
/obj/machinery/limbgrower/Initialize(mapload)
create_reagents(100, OPENCONTAINER)
stored_research = new /datum/techweb/specialized/autounlocking/limbgrower
. = ..()
AddComponent(/datum/component/plumbing/simple_demand)
/obj/machinery/limbgrower/ui_interact(mob/user, datum/tgui/ui)
. = ..()
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Limbgrower")
ui.open()
/obj/machinery/limbgrower/ui_state(mob/user)
return GLOB.physical_state
/obj/machinery/limbgrower/ui_data(mob/user)
var/list/data = list()
for(var/datum/reagent/reagent_id in reagents.reagent_list)
var/list/reagent_data = list(
reagent_name = reagent_id.name,
reagent_amount = reagent_id.volume,
reagent_type = reagent_id.type
)
data["reagents"] += list(reagent_data)
data["total_reagents"] = reagents.total_volume
data["max_reagents"] = reagents.maximum_volume
data["busy"] = busy
return data
/obj/machinery/limbgrower/ui_static_data(mob/user)
var/list/data = list()
data["categories"] = list()
var/species_categories = categories.Copy()
for(var/species in species_categories)
species_categories[species] = list()
for(var/design_id in stored_research.researched_designs)
var/datum/design/limb_design = SSresearch.techweb_design_by_id(design_id)
for(var/found_category in species_categories)
if(found_category in limb_design.category)
species_categories[found_category] += limb_design
for(var/category in species_categories)
var/list/category_data = list(
name = category,
designs = list(),
)
for(var/datum/design/found_design in species_categories[category])
var/list/all_reagents = list()
for(var/reagent_typepath in found_design.reagents_list)
var/datum/reagent/reagent_id = find_reagent_object_from_type(reagent_typepath)
var/list/reagent_data = list(
name = reagent_id.name,
amount = (found_design.reagents_list[reagent_typepath] * production_coefficient),
)
all_reagents += list(reagent_data)
category_data["designs"] += list(list(
parent_category = category,
name = found_design.name,
id = found_design.id,
needed_reagents = all_reagents,
))
data["categories"] += list(category_data)
return data
/obj/machinery/limbgrower/on_deconstruction()
for(var/obj/item/reagent_containers/glass/our_beaker in component_parts)
reagents.trans_to(our_beaker, our_beaker.reagents.maximum_volume)
..()
/obj/machinery/limbgrower/attackby(obj/item/user_item, mob/living/user, params)
if (busy)
to_chat(user, "<span class=\"alert\">The Limb Grower is busy. Please wait for completion of previous operation.</span>")
return
if(istype(user_item, /obj/item/disk/design_disk/limbs))
user.visible_message(span_notice("[user] begins to load \the [user_item] in \the [src]..."),
span_notice("You begin to load designs from \the [user_item]..."),
span_hear("You hear the clatter of a floppy drive."))
busy = TRUE
var/obj/item/disk/design_disk/limbs/limb_design_disk = user_item
if(do_after(user, 2 SECONDS, target = src))
for(var/datum/design/found_design in limb_design_disk.blueprints)
stored_research.add_design(found_design)
update_static_data(user)
busy = FALSE
return
if(default_deconstruction_screwdriver(user, "limbgrower_panelopen", "limbgrower_idleoff", user_item))
ui_close(user)
return
if(panel_open && default_deconstruction_crowbar(user_item))
return
if(user.combat_mode) //so we can hit the machine
return ..()
/obj/machinery/limbgrower/ui_act(action, list/params)
. = ..()
if(.)
return
if (busy)
to_chat(usr, span_danger("The limb grower is busy. Please wait for completion of previous operation."))
return
switch(action)
if("empty_reagent")
reagents.del_reagent(text2path(params["reagent_type"]))
. = TRUE
if("make_limb")
being_built = stored_research.isDesignResearchedID(params["design_id"])
if(!being_built)
CRASH("[src] was passed an invalid design id!")
/// All the reagents we're using to make our organ.
var/list/consumed_reagents_list = being_built.reagents_list.Copy()
/// The amount of power we're going to use, based on how much reagent we use.
var/power = 0
for(var/reagent_id in consumed_reagents_list)
consumed_reagents_list[reagent_id] *= production_coefficient
if(!reagents.has_reagent(reagent_id, consumed_reagents_list[reagent_id]))
audible_message(span_notice("The [src] buzzes."))
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
return
power = max(2000, (power + consumed_reagents_list[reagent_id]))
busy = TRUE
use_power(power)
flick("limbgrower_fill",src)
icon_state = "limbgrower_idleon"
selected_category = params["active_tab"]
addtimer(CALLBACK(src, .proc/build_item, consumed_reagents_list), production_speed * production_coefficient)
. = TRUE
return
/*
* The process of beginning to build a limb or organ.
* Goes through and sanity checks that we actually have enough reagent to build our item.
* Then, remove those reagents from our reagents datum.
*
* After the reagents are handled, we can proceede with making the limb or organ. (Limbs are handled in a separate proc)
*
* modified_consumed_reagents_list - the list of reagents we will consume on build, modified by the production coefficient.
*/
/obj/machinery/limbgrower/proc/build_item(list/modified_consumed_reagents_list)
for(var/reagent_id in modified_consumed_reagents_list)
if(!reagents.has_reagent(reagent_id, modified_consumed_reagents_list[reagent_id]))
audible_message(span_notice("The [src] buzzes."))
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
break
reagents.remove_reagent(reagent_id, modified_consumed_reagents_list[reagent_id])
var/built_typepath = being_built.build_path
// If we have a bodypart, we need to initialize the limb on its own. Otherwise we can build it here.
if(ispath(built_typepath, /obj/item/bodypart))
build_limb(built_typepath)
else
new built_typepath(loc)
busy = FALSE
flick("limbgrower_unfill", src)
icon_state = "limbgrower_idleoff"
/*
* The process of putting together a limb.
* This is called from after we remove the reagents, so this proc is just initializing the limb type.
*
* This proc handles skin / mutant color, greyscaling, names and descriptions, and various other limb creation steps.
*
* buildpath - the path of the bodypart we're building.
*/
/obj/machinery/limbgrower/proc/build_limb(buildpath)
/// The limb we're making with our buildpath, so we can edit it.
var/obj/item/bodypart/limb = new buildpath(loc)
/// Species with greyscale limbs.
var/list/greyscale_species = list(SPECIES_HUMAN, SPECIES_LIZARD, SPECIES_ETHEREAL)
if(selected_category in greyscale_species) //Species with greyscale parts should be included here
if(selected_category == SPECIES_HUMAN) //humans don't use the full colour spectrum, they use random_skin_tone
limb.skin_tone = random_skin_tone()
else
limb.species_color = random_short_color()
limb.icon = 'icons/mob/human_parts_greyscale.dmi'
limb.should_draw_greyscale = TRUE
else
limb.icon = 'icons/mob/human_parts.dmi'
// Set this limb up using the species name and body zone
limb.icon_state = "[selected_category]_[limb.body_zone]"
limb.name = "\improper biosynthetic [selected_category] [parse_zone(limb.body_zone)]"
limb.desc = "A synthetically produced [selected_category] limb, grown in a tube. This one is for the [parse_zone(limb.body_zone)]."
limb.species_id = selected_category
limb.update_icon_dropped()
limb.original_owner = WEAKREF(src) //prevents updating the icon, so a lizard arm on a human stays a lizard arm etc.
/obj/machinery/limbgrower/RefreshParts()
reagents.maximum_volume = 0
for(var/obj/item/reagent_containers/glass/our_beaker in component_parts)
reagents.maximum_volume += our_beaker.volume
our_beaker.reagents.trans_to(src, our_beaker.reagents.total_volume)
production_coefficient = 1.25
for(var/obj/item/stock_parts/manipulator/our_manipulator in component_parts)
production_coefficient -= our_manipulator.rating * 0.25
production_coefficient = clamp(production_coefficient, 0, 1) // coefficient goes from 1 -> 0.75 -> 0.5 -> 0.25
/obj/machinery/limbgrower/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
. += span_notice("The status display reads: Storing up to <b>[reagents.maximum_volume]u</b> of reagents.<br>Reagent consumption rate at <b>[production_coefficient * 100]%</b>.")
/*
* Checks our reagent list to see if a design can be built.
*
* limb_design - the design we're checking for buildability.
*
* returns TRUE if we have enough reagent to build it. Returns FALSE if we do not.
*/
/obj/machinery/limbgrower/proc/can_build(datum/design/limb_design)
for(var/datum/reagent/reagent_id in limb_design.reagents_list)
if(!reagents.has_reagent(reagent_id, limb_design.reagents_list[reagent_id] * production_coefficient))
return FALSE
return TRUE
/// Emagging a limbgrower allows you to build synthetic armblades.
/obj/machinery/limbgrower/emag_act(mob/user)
if(obj_flags & EMAGGED)
return
for(var/design_id in SSresearch.techweb_designs)
var/datum/design/found_design = SSresearch.techweb_design_by_id(design_id)
if((found_design.build_type & LIMBGROWER) && ("emagged" in found_design.category))
stored_research.add_design(found_design)
to_chat(user, span_warning("A warning flashes onto the screen, stating that safety overrides have been deactivated!"))
obj_flags |= EMAGGED
update_static_data(user)