mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2026-08-29 16:18:35 +01:00
Merge remote-tracking branch 'upstream/master' into tgui4.0-and-camera-console
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
plane = DIRTY_PLANE
|
||||
anchored = 1
|
||||
var/amount = 1
|
||||
generic_filth = TRUE
|
||||
persistent = FALSE
|
||||
|
||||
/obj/effect/decal/cleanable/liquid_fuel/New(turf/newLoc,amt=1,nologs=1)
|
||||
if(!nologs)
|
||||
|
||||
@@ -46,6 +46,8 @@ var/global/list/image/fluidtrack_cache=list()
|
||||
var/coming_state="blood1"
|
||||
var/going_state="blood2"
|
||||
var/updatedtracks=0
|
||||
persistent = TRUE
|
||||
generic_filth = FALSE
|
||||
|
||||
// dir = id in stack
|
||||
var/list/setdirs=list(
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
USAGE NOTE
|
||||
For decals, the var Persistent = 'has already been saved', and is primarily used to prevent duplicate savings of generic filth (filth.dm).
|
||||
This also means 'TRUE' can be used to define a decal as "Do not save at all, even as a generic replacement." if a dirt decal is considered 'too common' to save.
|
||||
generic_filth = TRUE means when the decal is saved, it will be switched out for a generic green 'filth' decal.
|
||||
*/
|
||||
|
||||
/obj/effect/decal/cleanable
|
||||
plane = DIRTY_PLANE
|
||||
var/persistent = FALSE
|
||||
|
||||
@@ -14,4 +14,15 @@
|
||||
|
||||
light_range = 5
|
||||
light_power = 3
|
||||
light_color = "#FFFFFF"
|
||||
light_color = "#FFFFFF"
|
||||
|
||||
/obj/effect/map_effect/perma_light/concentrated
|
||||
name = "permanent light (concentrated)"
|
||||
|
||||
light_range = 2
|
||||
light_power = 5
|
||||
|
||||
/obj/effect/map_effect/perma_light/concentrated/incandescent
|
||||
name = "permanent light (concentrated incandescent)"
|
||||
|
||||
light_color = LIGHT_COLOR_INCANDESCENT_TUBE
|
||||
@@ -0,0 +1,344 @@
|
||||
GLOBAL_LIST_EMPTY(all_portal_masters)
|
||||
|
||||
/*
|
||||
|
||||
Portal map effects allow a mapper to join two distant places together, while looking somewhat seamlessly connected.
|
||||
This can allow for very strange PoIs that twist and turn in what appear to be physically impossible ways.
|
||||
|
||||
Portals do have some specific requirements when mapping them in;
|
||||
- There must by one, and only one `/obj/effect/map_effect/portal/master` for each side of a portal.
|
||||
- Both sides need to have matching `portal_id`s in order to link to each other.
|
||||
- Each side must face opposite directions, e.g. if side A faces SOUTH, side B must face NORTH.
|
||||
- Each side must have the same orientation, e.g. horizontal on both sides, or vertical on both sides.
|
||||
- Portals can be made to be longer than 1x1 with `/obj/effect/map_effect/portal/line`s,
|
||||
but both sides must have the same length.
|
||||
- If portal lines are added, they must form a straight line and be next to a portal master or another portal line.
|
||||
- If portal lines are used, both portal masters should be in the same relative position among the lines.
|
||||
E.g. both being on the left most side on a horizontal row.
|
||||
|
||||
Portals also have some limitations to be aware of when mapping. Some of these are not an issue if you're trying to make an 'obvious' portal;
|
||||
- The objects seen through portals are purely visual, which has many implications,
|
||||
such as simple_mob AIs being blind to mobs on the other side of portals.
|
||||
- Objects on the other side of a portal can be interacted with if the interaction has no range limitation,
|
||||
or the distance between the two portal sides happens to be less than the interaction max range. Examine will probably work,
|
||||
while picking up an item that appears to be next to you will fail.
|
||||
- Sounds currently are not carried across portals.
|
||||
- Mismatched lighting between each portal end can make the portal look obvious.
|
||||
- Portals look weird when observing as a ghost, or otherwise when able to see through walls. Meson vision will also spoil the illusion.
|
||||
- Walls that change icons based on neightboring walls can give away that a portal is nearby if both sides don't have a similar transition.
|
||||
- Projectiles that pass through portals will generally work as intended, however aiming and firing upon someone on the other side of a portal
|
||||
will likely be weird due to the click targeting the real position of the thing clicked instead of the apparent position.
|
||||
Thrown objects suffer a similar fate.
|
||||
- The tiles that are visually shown across a portal are determined based on visibility at the time of portal initialization,
|
||||
and currently don't update, meaning that opacity changes are not reflected, e.g. a wall is deconstructed, or an airlock is opened.
|
||||
- There is currently a small but somewhat noticable pause in mob movement when moving across a portal,
|
||||
as a result of the mob's glide animation being inturrupted by a teleport.
|
||||
- Gas is not transferred through portals, and ZAS is oblivious to them.
|
||||
|
||||
A lot of those limitations can potentially be solved with some more work. Otherwise, portals work best in static environments like Points of Interest,
|
||||
when portals are shortly lived, or when portals are made to be obvious with special effects.
|
||||
*/
|
||||
|
||||
/obj/effect/map_effect/portal
|
||||
name = "portal subtype"
|
||||
invisibility = 0
|
||||
opacity = TRUE
|
||||
plane = TURF_PLANE
|
||||
layer = ABOVE_TURF_LAYER
|
||||
appearance_flags = PIXEL_SCALE|KEEP_TOGETHER // Removed TILE_BOUND so things not visible on the other side stay hidden from the viewer.
|
||||
|
||||
var/obj/effect/map_effect/portal/counterpart = null // The portal line or master that this is connected to, on the 'other side'.
|
||||
|
||||
// Information used to apply `pixel_[x|y]` offsets so that the visuals line up.
|
||||
// Set automatically by `calculate_dimensions()`.
|
||||
var/total_height = 0 // Measured in tiles.
|
||||
var/total_width = 0
|
||||
|
||||
var/portal_distance_x = 0 // How far the portal is from the left edge, in tiles.
|
||||
var/portal_distance_y = 0 // How far the portal is from the top edge.
|
||||
|
||||
/obj/effect/map_effect/portal/Destroy()
|
||||
vis_contents = null
|
||||
if(counterpart)
|
||||
counterpart.counterpart = null // Disconnect our counterpart from us
|
||||
counterpart = null // Now disconnect us from them.
|
||||
return ..()
|
||||
|
||||
// Called when something touches the portal, and usually teleports them to the other side.
|
||||
/obj/effect/map_effect/portal/Crossed(atom/movable/AM)
|
||||
if(AM.is_incorporeal())
|
||||
return
|
||||
..()
|
||||
if(!AM)
|
||||
return
|
||||
if(!counterpart)
|
||||
return
|
||||
|
||||
go_through_portal(AM)
|
||||
|
||||
|
||||
/obj/effect/map_effect/portal/proc/go_through_portal(atom/movable/AM)
|
||||
// TODO: Find a way to fake the glide or something.
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
if(L.pulling)
|
||||
var/atom/movable/pulled = L.pulling
|
||||
L.stop_pulling()
|
||||
// For some reason, trying to put the pulled object behind the person makes the drag stop and it doesn't even move to the other side.
|
||||
// pulled.forceMove(get_turf(counterpart))
|
||||
pulled.forceMove(counterpart.get_focused_turf())
|
||||
L.forceMove(counterpart.get_focused_turf())
|
||||
L.start_pulling(pulled)
|
||||
else
|
||||
L.forceMove(counterpart.get_focused_turf())
|
||||
else
|
||||
AM.forceMove(counterpart.get_focused_turf())
|
||||
|
||||
// 'Focused turf' is the turf directly in front of a portal,
|
||||
// and it is used both as the destination when crossing, as well as the PoV for visuals.
|
||||
/obj/effect/map_effect/portal/proc/get_focused_turf()
|
||||
return get_step(get_turf(src), dir)
|
||||
|
||||
// Determines the size of the block of turfs inside `vis_contents`, and where the portal is in relation to that.
|
||||
/obj/effect/map_effect/portal/proc/calculate_dimensions()
|
||||
var/highest_x = 0
|
||||
var/lowest_x = 0
|
||||
|
||||
var/highest_y = 0
|
||||
var/lowest_y = 0
|
||||
|
||||
// First pass is for finding the top right corner.
|
||||
for(var/thing in vis_contents)
|
||||
var/turf/T = thing
|
||||
if(T.x > highest_x)
|
||||
highest_x = T.x
|
||||
if(T.y > highest_y)
|
||||
highest_y = T.y
|
||||
|
||||
lowest_x = highest_x
|
||||
lowest_y = highest_y
|
||||
|
||||
// Second one is for the bottom left corner.
|
||||
for(var/thing in vis_contents)
|
||||
var/turf/T = thing
|
||||
if(T.x < lowest_x)
|
||||
lowest_x = T.x
|
||||
if(T.y < lowest_y)
|
||||
lowest_y = T.y
|
||||
|
||||
// Now calculate the dimensions.
|
||||
total_width = (highest_x - lowest_x) + 1
|
||||
total_height = (highest_y - lowest_y) + 1
|
||||
|
||||
// Find how far the portal is from the edges.
|
||||
var/turf/focused_T = counterpart.get_focused_turf()
|
||||
portal_distance_x = lowest_x - focused_T.x
|
||||
portal_distance_y = lowest_y - focused_T.y
|
||||
|
||||
|
||||
// Portal masters manage everything else involving portals.
|
||||
// This is the base type. Use `/side_a` or `/side_b` with matching IDs for actual portals.
|
||||
/obj/effect/map_effect/portal/master
|
||||
name = "portal master"
|
||||
show_messages = TRUE // So portals can hear and see, and relay to the other side.
|
||||
var/portal_id = "test" // For a portal to be made, both the A and B sides need to share the same ID value.
|
||||
var/list/portal_lines = list()
|
||||
|
||||
/obj/effect/map_effect/portal/master/Initialize()
|
||||
GLOB.all_portal_masters += src
|
||||
find_lines()
|
||||
..()
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/effect/map_effect/portal/master/LateInitialize()
|
||||
find_counterparts()
|
||||
make_visuals()
|
||||
apply_offset()
|
||||
|
||||
/obj/effect/map_effect/portal/master/Destroy()
|
||||
GLOB.all_portal_masters -= src
|
||||
for(var/thing in portal_lines)
|
||||
qdel(thing)
|
||||
return ..()
|
||||
|
||||
/obj/effect/map_effect/portal/master/proc/find_lines()
|
||||
var/list/dirs_to_search = list( turn(dir, 90), turn(dir, -90) )
|
||||
|
||||
for(var/dir_to_search in dirs_to_search)
|
||||
var/turf/current_T = get_turf(src)
|
||||
while(current_T)
|
||||
current_T = get_step(current_T, dir_to_search)
|
||||
var/obj/effect/map_effect/portal/line/line = locate() in current_T
|
||||
if(line)
|
||||
portal_lines += line
|
||||
line.my_master = src
|
||||
else
|
||||
break
|
||||
|
||||
// Connects both sides of a portal together.
|
||||
/obj/effect/map_effect/portal/master/proc/find_counterparts()
|
||||
for(var/thing in GLOB.all_portal_masters)
|
||||
var/obj/effect/map_effect/portal/master/M = thing
|
||||
if(M == src)
|
||||
continue
|
||||
if(M.counterpart)
|
||||
continue
|
||||
|
||||
if(M.portal_id == src.portal_id)
|
||||
counterpart = M
|
||||
M.counterpart = src
|
||||
if(portal_lines.len)
|
||||
for(var/i = 1 to portal_lines.len)
|
||||
var/obj/effect/map_effect/portal/line/our_line = portal_lines[i]
|
||||
var/obj/effect/map_effect/portal/line/their_line = M.portal_lines[i]
|
||||
our_line.counterpart = their_line
|
||||
their_line.counterpart = our_line
|
||||
break
|
||||
|
||||
if(!counterpart)
|
||||
crash_with("Portal master [type] ([x],[y],[z]) could not find another portal master with a matching portal_id ([portal_id]).")
|
||||
|
||||
/obj/effect/map_effect/portal/master/proc/make_visuals()
|
||||
var/list/observed_turfs = list()
|
||||
for(var/thing in portal_lines + src)
|
||||
var/obj/effect/map_effect/portal/P = thing
|
||||
P.name = null
|
||||
P.icon_state = null
|
||||
|
||||
if(!P.counterpart)
|
||||
return
|
||||
|
||||
var/turf/T = P.counterpart.get_focused_turf()
|
||||
P.vis_contents += T
|
||||
|
||||
var/list/things = dview(world.view, T)
|
||||
for(var/turf/turf in things)
|
||||
if(get_dir(turf, T) & P.dir)
|
||||
if(turf in observed_turfs) // Avoid showing the same turf twice or more for improved performance.
|
||||
continue
|
||||
|
||||
P.vis_contents += turf
|
||||
observed_turfs += turf
|
||||
|
||||
P.calculate_dimensions()
|
||||
|
||||
// Shifts the portal's pixels in order to line up properly, as BYOND offsets the sprite when it holds multiple turfs inside `vis_contents`.
|
||||
// This undos the shift that BYOND did.
|
||||
/obj/effect/map_effect/portal/master/proc/apply_offset()
|
||||
for(var/thing in portal_lines + src)
|
||||
var/obj/effect/map_effect/portal/P = thing
|
||||
|
||||
P.pixel_x = WORLD_ICON_SIZE * P.portal_distance_x
|
||||
P.pixel_y = WORLD_ICON_SIZE * P.portal_distance_y
|
||||
|
||||
// Allows portals to transfer emotes.
|
||||
// Only portal masters do this to avoid flooding the other side with duplicate messages.
|
||||
/obj/effect/map_effect/portal/master/see_emote(mob/M, text)
|
||||
if(!counterpart)
|
||||
return
|
||||
var/turf/T = counterpart.get_focused_turf()
|
||||
var/list/in_range = get_mobs_and_objs_in_view_fast(T, world.view, 0)
|
||||
var/list/mobs_to_relay = in_range["mobs"]
|
||||
|
||||
for(var/thing in mobs_to_relay)
|
||||
var/mob/mob = thing
|
||||
var/rendered = "<span class='message'>[text]</span>"
|
||||
mob.show_message(rendered)
|
||||
|
||||
..()
|
||||
|
||||
// Allows portals to transfer visible messages.
|
||||
/obj/effect/map_effect/portal/master/show_message(msg, type, alt, alt_type)
|
||||
if(!counterpart)
|
||||
return
|
||||
var/rendered = "<span class='message'>[msg]</span>"
|
||||
var/turf/T = counterpart.get_focused_turf()
|
||||
var/list/in_range = get_mobs_and_objs_in_view_fast(T, world.view, 0)
|
||||
var/list/mobs_to_relay = in_range["mobs"]
|
||||
|
||||
for(var/thing in mobs_to_relay)
|
||||
var/mob/mob = thing
|
||||
mob.show_message(rendered)
|
||||
|
||||
..()
|
||||
|
||||
// Allows portals to transfer speech.
|
||||
/obj/effect/map_effect/portal/master/hear_talk(mob/M, list/message_pieces, verb)
|
||||
if(!counterpart)
|
||||
return
|
||||
var/turf/T = counterpart.get_focused_turf()
|
||||
var/list/in_range = get_mobs_and_objs_in_view_fast(T, world.view, 0)
|
||||
var/list/mobs_to_relay = in_range["mobs"]
|
||||
|
||||
for(var/thing in mobs_to_relay)
|
||||
var/mob/mob = thing
|
||||
var/message = mob.combine_message(message_pieces, verb, M)
|
||||
var/name_used = M.GetVoice()
|
||||
var/rendered = null
|
||||
rendered = "<span class='game say'><span class='name'>[name_used]</span> [message]</span>"
|
||||
mob.show_message(rendered, 2)
|
||||
|
||||
..()
|
||||
|
||||
// Returns the position that an atom that's hopefully on the other side of the portal would be if it were really there.
|
||||
// Z levels not taken into account.
|
||||
/obj/effect/map_effect/portal/master/proc/get_apparent_position(atom/A)
|
||||
if(!counterpart)
|
||||
return null
|
||||
|
||||
var/turf/true_turf = get_turf(A)
|
||||
var/obj/effect/map_effect/portal/master/other_master = counterpart
|
||||
|
||||
var/in_vis_contents = FALSE
|
||||
for(var/thing in other_master.portal_lines + other_master)
|
||||
var/obj/effect/map_effect/portal/P = thing
|
||||
if(P in true_turf.vis_locs)
|
||||
in_vis_contents = TRUE
|
||||
break
|
||||
|
||||
if(!in_vis_contents)
|
||||
return null // Not in vision of the other portal.
|
||||
|
||||
var/turf/their_focus = counterpart.get_focused_turf()
|
||||
var/turf/our_focus = get_focused_turf()
|
||||
|
||||
var/relative_x = (true_turf.x - our_focus.x)
|
||||
relative_x += SIGN(relative_x)
|
||||
var/relative_y = (true_turf.y - our_focus.y)
|
||||
relative_y += SIGN(relative_y)
|
||||
|
||||
return new /datum/position(their_focus.x + relative_x, their_focus.y + relative_y, our_focus.z)
|
||||
|
||||
|
||||
/obj/effect/map_effect/portal/master/side_a
|
||||
name = "portal master A"
|
||||
icon_state = "portal_side_a"
|
||||
// color = "#00FF00"
|
||||
|
||||
/obj/effect/map_effect/portal/master/side_b
|
||||
name = "portal master B"
|
||||
icon_state = "portal_side_b"
|
||||
// color = "#FF0000"
|
||||
|
||||
|
||||
|
||||
// Portal lines extend out from the sides of portal masters,
|
||||
// They let portals be longer than 1x1.
|
||||
// Both sides MUST be the same length, meaning if side A is 1x3, side B must also be 1x3.
|
||||
/obj/effect/map_effect/portal/line
|
||||
name = "portal line"
|
||||
var/obj/effect/map_effect/portal/master/my_master = null
|
||||
|
||||
/obj/effect/map_effect/portal/line/Destroy()
|
||||
if(my_master)
|
||||
my_master.portal_lines -= src
|
||||
my_master = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/map_effect/portal/line/side_a
|
||||
name = "portal line A"
|
||||
icon_state = "portal_line_side_a"
|
||||
|
||||
/obj/effect/map_effect/portal/line/side_b
|
||||
name = "portal line B"
|
||||
icon_state = "portal_line_side_b"
|
||||
@@ -332,14 +332,14 @@
|
||||
if(!heart)
|
||||
return TRUE
|
||||
|
||||
var/blood_volume = round((H.vessel.get_reagent_amount("blood")/H.species.blood_volume)*100)
|
||||
var/blood_volume = H.vessel.get_reagent_amount("blood")
|
||||
if(!heart || heart.is_broken())
|
||||
blood_volume *= 0.3
|
||||
else if(heart.is_bruised())
|
||||
blood_volume *= 0.7
|
||||
else if(heart.damage > 1)
|
||||
blood_volume *= 0.8
|
||||
return blood_volume < BLOOD_VOLUME_SURVIVE
|
||||
return blood_volume < H.species.blood_volume*H.species.blood_level_fatal
|
||||
|
||||
/obj/item/weapon/shockpaddles/proc/check_charge(var/charge_amt)
|
||||
return 0
|
||||
|
||||
@@ -130,10 +130,10 @@ HALOGEN COUNTER - Radcount on mobs
|
||||
for(var/A in C.reagents.reagent_list)
|
||||
var/datum/reagent/R = A
|
||||
if(R.scannable)
|
||||
reagentdata["[R.id]"] = "<span class='notice'>\t[round(C.reagents.get_reagent_amount(R.id), 1)]u [R.name]</span><br>"
|
||||
reagentdata["[R.id]"] = "<span class='notice'>\t[round(C.reagents.get_reagent_amount(R.id), 1)]u [R.name][(R.overdose && R.volume > R.overdose) ? " - <span class='danger'>Overdose</span>" : ""]</span><br>"
|
||||
else
|
||||
unknown++
|
||||
unknownreagents["[R.id]"] = "<span class='notice'>\t[round(C.reagents.get_reagent_amount(R.id), 1)]u [R.name]</span><br>"
|
||||
unknownreagents["[R.id]"] = "<span class='notice'>\t[round(C.reagents.get_reagent_amount(R.id), 1)]u [R.name][(R.overdose && R.volume > R.overdose) ? " - <span class='danger'>Overdose</span>" : ""]</span><br>"
|
||||
if(reagentdata.len)
|
||||
dat += "<span class='notice'>Beneficial reagents detected in subject's blood:</span><br>"
|
||||
for(var/d in reagentdata)
|
||||
@@ -150,14 +150,14 @@ HALOGEN COUNTER - Radcount on mobs
|
||||
var/stomachreagentdata[0]
|
||||
var/stomachunknownreagents[0]
|
||||
for(var/B in C.ingested.reagent_list)
|
||||
var/datum/reagent/T = B
|
||||
if(T.scannable)
|
||||
stomachreagentdata["[T.id]"] = "<span class='notice'>\t[round(C.ingested.get_reagent_amount(T.id), 1)]u [T.name]</span><br>"
|
||||
var/datum/reagent/R = B
|
||||
if(R.scannable)
|
||||
stomachreagentdata["[R.id]"] = "<span class='notice'>\t[round(C.ingested.get_reagent_amount(R.id), 1)]u [R.name][(R.overdose && R.volume > R.overdose) ? " - <span class='danger'>Overdose</span>" : ""]</span><br>"
|
||||
if (advscan == 0 || showadvscan == 0)
|
||||
dat += "<span class='notice'>[T.name] found in subject's stomach.</span><br>"
|
||||
dat += "<span class='notice'>[R.name] found in subject's stomach.</span><br>"
|
||||
else
|
||||
++unknown
|
||||
stomachunknownreagents["[T.id]"] = "<span class='notice'>\t[round(C.ingested.get_reagent_amount(T.id), 1)]u [T.name]</span><br>"
|
||||
stomachunknownreagents["[R.id]"] = "<span class='notice'>\t[round(C.ingested.get_reagent_amount(R.id), 1)]u [R.name][(R.overdose && R.volume > R.overdose) ? " - <span class='danger'>Overdose</span>" : ""]</span><br>"
|
||||
if(advscan >= 1 && showadvscan == 1)
|
||||
dat += "<span class='notice'>Beneficial reagents detected in subject's stomach:</span><br>"
|
||||
for(var/d in stomachreagentdata)
|
||||
@@ -174,14 +174,14 @@ HALOGEN COUNTER - Radcount on mobs
|
||||
var/touchreagentdata[0]
|
||||
var/touchunknownreagents[0]
|
||||
for(var/B in C.touching.reagent_list)
|
||||
var/datum/reagent/T = B
|
||||
if(T.scannable)
|
||||
touchreagentdata["[T.id]"] = "<span class='notice'>\t[round(C.touching.get_reagent_amount(T.id), 1)]u [T.name]</span><br>"
|
||||
var/datum/reagent/R = B
|
||||
if(R.scannable)
|
||||
touchreagentdata["[R.id]"] = "<span class='notice'>\t[round(C.touching.get_reagent_amount(R.id), 1)]u [R.name][(R.overdose && R.can_overdose_touch && R.volume > R.overdose) ? " - <span class='danger'>Overdose</span>" : ""]</span><br>"
|
||||
if (advscan == 0 || showadvscan == 0)
|
||||
dat += "<span class='notice'>[T.name] found in subject's dermis.</span><br>"
|
||||
dat += "<span class='notice'>[R.name] found in subject's dermis.</span><br>"
|
||||
else
|
||||
++unknown
|
||||
touchunknownreagents["[T.id]"] = "<span class='notice'>\t[round(C.ingested.get_reagent_amount(T.id), 1)]u [T.name]</span><br>"
|
||||
touchunknownreagents["[R.id]"] = "<span class='notice'>\t[round(C.ingested.get_reagent_amount(R.id), 1)]u [R.name][(R.overdose && R.can_overdose_touch && R.volume > R.overdose) ? " - <span class='danger'>Overdose</span>" : ""]</span><br>"
|
||||
if(advscan >= 1 && showadvscan == 1)
|
||||
dat += "<span class='notice'>Beneficial reagents detected in subject's dermis:</span><br>"
|
||||
for(var/d in touchreagentdata)
|
||||
@@ -266,9 +266,11 @@ HALOGEN COUNTER - Radcount on mobs
|
||||
var/blood_volume = H.vessel.get_reagent_amount("blood")
|
||||
var/blood_percent = round((blood_volume / H.species.blood_volume)*100)
|
||||
var/blood_type = H.dna.b_type
|
||||
if(blood_percent <= BLOOD_VOLUME_BAD)
|
||||
if(blood_volume <= H.species.blood_volume*H.species.blood_level_danger)
|
||||
dat += "<span class='danger'><i>Warning: Blood Level CRITICAL: [blood_percent]% [blood_volume]cl. Type: [blood_type]</i></span><br>"
|
||||
else if(blood_percent <= BLOOD_VOLUME_SAFE)
|
||||
else if(blood_volume <= H.species.blood_volume*H.species.blood_level_warning)
|
||||
dat += "<span class='danger'><i>Warning: Blood Level VERY LOW: [blood_percent]% [blood_volume]cl. Type: [blood_type]</i></span><br>"
|
||||
else if(blood_volume <= H.species.blood_volume*H.species.blood_level_safe)
|
||||
dat += "<span class='danger'>Warning: Blood Level LOW: [blood_percent]% [blood_volume]cl. Type: [blood_type]</span><br>"
|
||||
else
|
||||
dat += "<span class='notice'>Blood Level Normal: [blood_percent]% [blood_volume]cl. Type: [blood_type]</span><br>"
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
|
||||
#define JAR_NOTHING 0
|
||||
#define JAR_MONEY 1
|
||||
#define JAR_ANIMAL 2
|
||||
#define JAR_SPIDER 3
|
||||
|
||||
/obj/item/glass_jar
|
||||
name = "glass jar"
|
||||
desc = "A small empty jar."
|
||||
@@ -27,7 +33,7 @@
|
||||
var/mob/L = A
|
||||
user.visible_message("<span class='notice'>[user] scoops [L] into \the [src].</span>", "<span class='notice'>You scoop [L] into \the [src].</span>")
|
||||
L.loc = src
|
||||
contains = 2
|
||||
contains = JAR_ANIMAL
|
||||
update_icon()
|
||||
return
|
||||
else if(istype(A, /obj/effect/spider/spiderling))
|
||||
@@ -35,40 +41,40 @@
|
||||
user.visible_message("<span class='notice'>[user] scoops [S] into \the [src].</span>", "<span class='notice'>You scoop [S] into \the [src].</span>")
|
||||
S.loc = src
|
||||
STOP_PROCESSING(SSobj, S) // No growing inside jars
|
||||
contains = 3
|
||||
contains = JAR_SPIDER
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/obj/item/glass_jar/attack_self(var/mob/user)
|
||||
switch(contains)
|
||||
if(1)
|
||||
if(JAR_MONEY)
|
||||
for(var/obj/O in src)
|
||||
O.loc = user.loc
|
||||
to_chat(user, "<span class='notice'>You take money out of \the [src].</span>")
|
||||
contains = 0
|
||||
contains = JAR_NOTHING
|
||||
update_icon()
|
||||
return
|
||||
if(2)
|
||||
if(JAR_ANIMAL)
|
||||
for(var/mob/M in src)
|
||||
M.loc = user.loc
|
||||
user.visible_message("<span class='notice'>[user] releases [M] from \the [src].</span>", "<span class='notice'>You release [M] from \the [src].</span>")
|
||||
contains = 0
|
||||
contains = JAR_NOTHING
|
||||
update_icon()
|
||||
return
|
||||
if(3)
|
||||
if(JAR_SPIDER)
|
||||
for(var/obj/effect/spider/spiderling/S in src)
|
||||
S.loc = user.loc
|
||||
user.visible_message("<span class='notice'>[user] releases [S] from \the [src].</span>", "<span class='notice'>You release [S] from \the [src].</span>")
|
||||
START_PROCESSING(SSobj, S) // They can grow after being let out though
|
||||
contains = 0
|
||||
contains = JAR_NOTHING
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/obj/item/glass_jar/attackby(var/obj/item/W, var/mob/user)
|
||||
if(istype(W, /obj/item/weapon/spacecash))
|
||||
if(contains == 0)
|
||||
contains = 1
|
||||
if(contains != 1)
|
||||
if(contains == JAR_NOTHING)
|
||||
contains = JAR_MONEY
|
||||
if(contains != JAR_MONEY)
|
||||
return
|
||||
var/obj/item/weapon/spacecash/S = W
|
||||
user.visible_message("<span class='notice'>[user] puts [S.worth] [S.worth > 1 ? "thalers" : "thaler"] into \the [src].</span>")
|
||||
@@ -80,10 +86,10 @@
|
||||
underlays.Cut()
|
||||
overlays.Cut()
|
||||
switch(contains)
|
||||
if(0)
|
||||
if(JAR_NOTHING)
|
||||
name = initial(name)
|
||||
desc = initial(desc)
|
||||
if(1)
|
||||
if(JAR_MONEY)
|
||||
name = "tip jar"
|
||||
desc = "A small jar with money inside."
|
||||
for(var/obj/item/weapon/spacecash/S in src)
|
||||
@@ -92,7 +98,7 @@
|
||||
money.pixel_y = rand(-6, 6)
|
||||
money.transform *= 0.6
|
||||
underlays += money
|
||||
if(2)
|
||||
if(JAR_ANIMAL)
|
||||
for(var/mob/M in src)
|
||||
var/image/victim = image(M.icon, M.icon_state)
|
||||
victim.pixel_y = 6
|
||||
@@ -105,10 +111,109 @@
|
||||
underlays += victim
|
||||
name = "glass jar with [M]"
|
||||
desc = "A small jar with [M] inside."
|
||||
if(3)
|
||||
if(JAR_SPIDER)
|
||||
for(var/obj/effect/spider/spiderling/S in src)
|
||||
var/image/victim = image(S.icon, S.icon_state)
|
||||
underlays += victim
|
||||
name = "glass jar with [S]"
|
||||
desc = "A small jar with [S] inside."
|
||||
return
|
||||
return
|
||||
|
||||
/obj/item/glass_jar/fish
|
||||
name = "glass tank"
|
||||
desc = "A large glass tank."
|
||||
|
||||
var/filled = FALSE
|
||||
|
||||
w_class = ITEMSIZE_NORMAL
|
||||
|
||||
accept_mobs = list(/mob/living/simple_mob/animal/passive/lizard, /mob/living/simple_mob/animal/passive/mouse, /mob/living/simple_mob/animal/sif/leech, /mob/living/simple_mob/animal/sif/frostfly, /mob/living/simple_mob/animal/sif/glitterfly, /mob/living/simple_mob/animal/passive/fish)
|
||||
|
||||
/obj/item/glass_jar/fish/plastic
|
||||
name = "plastic tank"
|
||||
desc = "A large plastic tank."
|
||||
matter = list("plastic" = 4000)
|
||||
|
||||
/obj/item/glass_jar/fish/update_icon() // Also updates name and desc
|
||||
underlays.Cut()
|
||||
overlays.Cut()
|
||||
|
||||
if(filled)
|
||||
underlays += image(icon, "[icon_state]_water")
|
||||
|
||||
switch(contains)
|
||||
if(JAR_NOTHING)
|
||||
name = initial(name)
|
||||
desc = initial(desc)
|
||||
if(JAR_MONEY)
|
||||
name = "tip tank"
|
||||
desc = "A large [name] with money inside."
|
||||
for(var/obj/item/weapon/spacecash/S in src)
|
||||
var/image/money = image(S.icon, S.icon_state)
|
||||
money.pixel_x = rand(-2, 3)
|
||||
money.pixel_y = rand(-6, 6)
|
||||
money.transform *= 0.6
|
||||
underlays += money
|
||||
if(JAR_ANIMAL)
|
||||
for(var/mob/M in src)
|
||||
var/image/victim = image(M.icon, M.icon_state)
|
||||
var/initial_x_scale = M.icon_scale_x
|
||||
var/initial_y_scale = M.icon_scale_y
|
||||
M.adjust_scale(0.7)
|
||||
victim.appearance = M.appearance
|
||||
M.adjust_scale(initial_x_scale, initial_y_scale)
|
||||
victim.pixel_y = 4
|
||||
underlays += victim
|
||||
name = "[name] with [M]"
|
||||
desc = "A large [name] with [M] inside."
|
||||
if(JAR_SPIDER)
|
||||
for(var/obj/effect/spider/spiderling/S in src)
|
||||
var/image/victim = image(S.icon, S.icon_state)
|
||||
underlays += victim
|
||||
name = "[name] with [S]"
|
||||
desc = "A large tank with [S] inside."
|
||||
|
||||
if(filled)
|
||||
desc = "[desc] It contains water."
|
||||
|
||||
return
|
||||
|
||||
/obj/item/glass_jar/fish/afterattack(var/atom/A, var/mob/user, var/proximity)
|
||||
if(!filled)
|
||||
if(istype(A, /obj/structure/sink) || istype(A, /turf/simulated/floor/water))
|
||||
if(contains && user.a_intent == "help")
|
||||
to_chat(user, "<span class='warning'>That probably isn't the best idea.</span>")
|
||||
return
|
||||
|
||||
to_chat(user, "<span class='notice'>You fill \the [src] with water!</span>")
|
||||
filled = TRUE
|
||||
update_icon()
|
||||
return
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/item/glass_jar/fish/attack_self(var/mob/user)
|
||||
if(filled)
|
||||
if(contains == JAR_ANIMAL)
|
||||
if(user.a_intent == "help")
|
||||
to_chat(user, "<span class='notice'>Maybe you shouldn't empty the water...</span>")
|
||||
return
|
||||
|
||||
else
|
||||
filled = FALSE
|
||||
user.visible_message("<span class='warning'>[user] dumps out \the [src]'s water!</span>")
|
||||
update_icon()
|
||||
return
|
||||
|
||||
else
|
||||
user.visible_message("<span class='notice'>[user] dumps \the [src]'s water.</span>")
|
||||
filled = FALSE
|
||||
update_icon()
|
||||
return
|
||||
|
||||
return ..()
|
||||
|
||||
#undef JAR_NOTHING
|
||||
#undef JAR_MONEY
|
||||
#undef JAR_ANIMAL
|
||||
#undef JAR_SPIDER
|
||||
|
||||
@@ -425,6 +425,15 @@
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/random/mech_toy
|
||||
name = "Random Mech Toy"
|
||||
desc = "This is a random mech toy."
|
||||
icon = 'icons/obj/toy.dmi'
|
||||
icon_state = "ripleytoy"
|
||||
|
||||
/obj/random/mech_toy/item_to_spawn()
|
||||
return pick(typesof(/obj/item/toy/prize))
|
||||
|
||||
/obj/item/toy/prize/ripley
|
||||
name = "toy ripley"
|
||||
desc = "Mini-Mecha action figure! Collect them all! 1/11."
|
||||
|
||||
@@ -198,6 +198,16 @@
|
||||
/obj/item/weapon/storage/box/empshells/large
|
||||
starts_with = list(/obj/item/ammo_casing/a12g/emp = 16)
|
||||
|
||||
/obj/item/weapon/storage/box/flechetteshells
|
||||
name = "box of shotgun flechettes"
|
||||
desc = "It has a picture of a gun and several warning symbols on the front.<br>WARNING: Live ammunition. Misuse may result in serious injury or death."
|
||||
icon_state = "lethalslug_box"
|
||||
item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit")
|
||||
starts_with = list(/obj/item/ammo_casing/a12g/flechette = 8)
|
||||
|
||||
/obj/item/weapon/storage/box/flechetteshells/large
|
||||
starts_with = list(/obj/item/ammo_casing/a12g/flechette = 16)
|
||||
|
||||
/obj/item/weapon/storage/box/sniperammo
|
||||
name = "box of 14.5mm shells"
|
||||
desc = "It has a picture of a gun and several warning symbols on the front.<br>WARNING: Live ammunition. Misuse may result in serious injury or death."
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
overlays.Cut()
|
||||
if(front_id)
|
||||
var/tiny_state = "id-generic"
|
||||
if("id-"+front_id.icon_state in icon_states(icon))
|
||||
if("id-"+front_id.icon_state in cached_icon_states(icon))
|
||||
tiny_state = "id-"+front_id.icon_state
|
||||
var/image/tiny_image = new/image(icon, icon_state = tiny_state)
|
||||
tiny_image.appearance_flags = RESET_COLOR
|
||||
|
||||
@@ -33,7 +33,9 @@
|
||||
prob(10);/mob/living/simple_mob/animal/passive/mouse,
|
||||
prob(10);/mob/living/simple_mob/animal/passive/yithian,
|
||||
prob(10);/mob/living/simple_mob/animal/passive/tindalos,
|
||||
prob(10);/mob/living/simple_mob/animal/passive/pillbug,
|
||||
prob(10);/mob/living/simple_mob/animal/passive/dog/tamaskan,
|
||||
prob(10);/mob/living/simple_mob/animal/passive/dog/brittany,
|
||||
prob(3);/mob/living/simple_mob/animal/passive/bird/parrot,
|
||||
prob(1);/mob/living/simple_mob/animal/passive/crab)
|
||||
|
||||
@@ -69,10 +71,12 @@
|
||||
|
||||
/obj/random/mob/sif/item_to_spawn()
|
||||
return pick(prob(30);/mob/living/simple_mob/animal/sif/diyaab,
|
||||
prob(20);/mob/living/simple_mob/animal/passive/hare,
|
||||
prob(15);/mob/living/simple_mob/animal/passive/crab,
|
||||
prob(15);/mob/living/simple_mob/animal/passive/penguin,
|
||||
prob(15);/mob/living/simple_mob/animal/passive/mouse,
|
||||
prob(15);/mob/living/simple_mob/animal/passive/dog/tamaskan,
|
||||
prob(10);/mob/living/simple_mob/animal/sif/siffet,
|
||||
prob(2);/mob/living/simple_mob/animal/giant_spider/frost,
|
||||
prob(1);/mob/living/simple_mob/animal/space/goose,
|
||||
prob(20);/mob/living/simple_mob/animal/passive/crab)
|
||||
@@ -88,6 +92,7 @@
|
||||
|
||||
/obj/random/mob/sif/peaceful/item_to_spawn()
|
||||
return pick(prob(30);/mob/living/simple_mob/animal/sif/diyaab,
|
||||
prob(20);/mob/living/simple_mob/animal/passive/hare,
|
||||
prob(15);/mob/living/simple_mob/animal/passive/crab,
|
||||
prob(15);/mob/living/simple_mob/animal/passive/penguin,
|
||||
prob(15);/mob/living/simple_mob/animal/passive/mouse,
|
||||
@@ -102,6 +107,8 @@
|
||||
/obj/random/mob/sif/hostile/item_to_spawn()
|
||||
return pick(prob(22);/mob/living/simple_mob/animal/sif/savik,
|
||||
prob(33);/mob/living/simple_mob/animal/giant_spider/frost,
|
||||
prob(20);/mob/living/simple_mob/animal/sif/frostfly,
|
||||
prob(10);/mob/living/simple_mob/animal/sif/tymisian,
|
||||
prob(45);/mob/living/simple_mob/animal/sif/shantak)
|
||||
|
||||
/obj/random/mob/sif/kururak
|
||||
@@ -329,6 +336,12 @@
|
||||
/mob/living/simple_mob/animal/sif/duck,
|
||||
/mob/living/simple_mob/animal/sif/duck
|
||||
),
|
||||
prob(15);list(
|
||||
/mob/living/simple_mob/animal/passive/hare,
|
||||
/mob/living/simple_mob/animal/passive/hare,
|
||||
/mob/living/simple_mob/animal/passive/hare,
|
||||
/mob/living/simple_mob/animal/passive/hare
|
||||
),
|
||||
prob(10);list(
|
||||
/mob/living/simple_mob/animal/sif/shantak/retaliate,
|
||||
/mob/living/simple_mob/animal/sif/shantak/retaliate,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
var/cult = 0
|
||||
|
||||
/obj/structure/sign/double/barsign/proc/get_valid_states(initial=1)
|
||||
. = icon_states(icon)
|
||||
. = cached_icon_states(icon)
|
||||
. -= "on"
|
||||
. -= "narsiebistro"
|
||||
. -= "empty"
|
||||
|
||||
@@ -114,7 +114,7 @@ two tiles on initialization, and which way a cliff is facing may change during m
|
||||
|
||||
var/subtraction_icon_state = "[icon_state]-subtract"
|
||||
var/cache_string = "[icon_state]_[T.icon]_[T.icon_state]"
|
||||
if(T && subtraction_icon_state in icon_states(icon))
|
||||
if(T && subtraction_icon_state in cached_icon_states(icon))
|
||||
cut_overlays()
|
||||
// If we've made the same icon before, just recycle it.
|
||||
if(cache_string in GLOB.cliff_icon_cache)
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
else
|
||||
. += "<span class='notice'>There is a thick layer of silicate covering it.</span>"
|
||||
|
||||
/obj/structure/window/examine_icon()
|
||||
return icon(icon=initial(icon),icon_state=initial(icon_state))
|
||||
|
||||
/obj/structure/window/take_damage(var/damage = 0, var/sound_effect = 1)
|
||||
var/initialhealth = health
|
||||
|
||||
|
||||
Reference in New Issue
Block a user