Skip to content

Commit

Permalink
Камеры Планшеты, DNG файлы. (#2520)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lexanx authored Aug 4, 2024
1 parent 4e54133 commit 5938853
Show file tree
Hide file tree
Showing 18 changed files with 442 additions and 246 deletions.
330 changes: 182 additions & 148 deletions code/_helpers/icons.dm
Original file line number Diff line number Diff line change
Expand Up @@ -418,157 +418,168 @@ Get flat icon by DarkCampainger. As it says on the tin, will return an icon with
as a single icon. Useful for when you want to manipulate an icon via the above as overlays are not normally included.
The _flatIcons list is a cache for generated icon files.
*/
//[SIERRA-EDIT]
/proc/getFlatIcon(image/appearance, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE)
// Loop through the underlays, then overlays, sorting them into the layers list
#define PROCESS_OVERLAYS_OR_UNDERLAYS(flat, process, base_layer) \
for (var/i in 1 to LAZYLEN(process)) { \
var/image/current = process[i]; \
if (!current) { \
continue; \
} \
if (current.plane != FLOAT_PLANE && current.plane != appearance.plane) { \
continue; \
} \
var/current_layer = current.layer; \
if (current_layer < 0) { \
if (current_layer <= -1000) { \
return flat; \
} \
current_layer = base_layer + appearance.layer + current_layer / 1000; \
} \
for (var/index_to_compare_to in 1 to LAZYLEN(layers)) { \
var/compare_to = layers[index_to_compare_to]; \
if (current_layer < layers[compare_to]) { \
layers.Insert(index_to_compare_to, current); \
break; \
} \
} \
layers[current] = current_layer; \
}

var/static/icon/flat_template = icon('icons/blanks/32x32.dmi', "nothing")

if(!appearance || appearance.alpha <= 0)
return icon(flat_template)

if(start)
if(!defdir)
defdir = appearance.dir
if(!deficon)
deficon = appearance.icon
if(!defstate)
defstate = appearance.icon_state
if(!defblend)
defblend = appearance.blend_mode

var/curicon = appearance.icon || deficon
var/curstate = appearance.icon_state || defstate
var/curdir = (!appearance.dir || appearance.dir == SOUTH) ? defdir : appearance.dir

var/render_icon = curicon

if (render_icon)
var/curstates = icon_states(curicon)
if(!(curstate in curstates))
if ("" in curstates)
curstate = ""
else
render_icon = FALSE

var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have

//Try to remove/optimize this section ASAP, CPU hog.
//Determines if there's directionals.
if(render_icon && curdir != SOUTH)
if (
!length(icon_states(icon(curicon, curstate, NORTH))) \
&& !length(icon_states(icon(curicon, curstate, EAST))) \
&& !length(icon_states(icon(curicon, curstate, WEST))) \
)
base_icon_dir = SOUTH

if(!base_icon_dir)
base_icon_dir = curdir

var/curblend = appearance.blend_mode || defblend

if(LAZYLEN(appearance.overlays) || LAZYLEN(appearance.underlays))
var/icon/flat = icon(flat_template)
// Layers will be a sorted list of icons/overlays, based on the order in which they are displayed
var/list/layers = list()
var/image/copy
// Add the atom's icon itself, without pixel_x/y offsets.
if(render_icon)
copy = image(icon=curicon, icon_state=curstate, layer=appearance.layer, dir=base_icon_dir)
copy.color = appearance.color
copy.alpha = appearance.alpha
copy.blend_mode = curblend
layers[copy] = appearance.layer

PROCESS_OVERLAYS_OR_UNDERLAYS(flat, appearance.underlays, 0)
PROCESS_OVERLAYS_OR_UNDERLAYS(flat, appearance.overlays, 1)

var/icon/add // Icon of overlay being added

var/flatX1 = 1
var/flatX2 = flat.Width()
var/flatY1 = 1
var/flatY2 = flat.Height()

var/addX1 = 0
var/addX2 = 0
var/addY1 = 0
var/addY2 = 0

for(var/image/layer_image as anything in layers)
if(layer_image.alpha == 0)
continue

/proc/getFlatIcon(image/A, defdir=2, deficon=null, defstate="", defblend=BLEND_DEFAULT, always_use_defdir = 0)
RETURN_TYPE(/icon)
// We start with a blank canvas, otherwise some icon procs crash silently
var/icon/flat = icon('icons/effects/effects.dmi', "icon_state"="nothing") // Final flattened icon
if(!A)
return flat
if(A.alpha <= 0)
return flat
var/noIcon = FALSE

var/curicon
if(A.icon)
curicon = A.icon
else
curicon = deficon
if(layer_image == copy) // 'layer_image' is an /image based on the object being flattened.
curblend = BLEND_OVERLAY
add = icon(layer_image.icon, layer_image.icon_state, base_icon_dir)
else // 'I' is an appearance object.
add = getFlatIcon(image(layer_image), curdir, curicon, curstate, curblend, FALSE, no_anim)
if(!add)
continue

if(!curicon)
noIcon = TRUE // Do not render this object.
// Find the new dimensions of the flat icon to fit the added overlay
addX1 = min(flatX1, layer_image.pixel_x + 1)
addX2 = max(flatX2, layer_image.pixel_x + add.Width())
addY1 = min(flatY1, layer_image.pixel_y + 1)
addY2 = max(flatY2, layer_image.pixel_y + add.Height())

var/curstate
if(A.icon_state)
curstate = A.icon_state
else
curstate = defstate
if(addX1!=flatX1 || addX2!=flatX2 || addY1!=flatY1 || addY2!=flatY2)
// Resize the flattened icon so the new icon fits
flat.Crop(addX1-flatX1+1, addY1-flatY1+1, addX2-flatX1+1, addY2-flatY1+1)
flatX1=addX1;flatX2=addX2
flatY1=addY1;flatY2=addY2

if(!noIcon && !(curstate in icon_states(curicon)))
if("" in icon_states(curicon))
curstate = ""
else
noIcon = TRUE // Do not render this object.
// Blend the overlay into the flattened icon
flat.Blend(add, blendMode2iconMode(curblend), layer_image.pixel_x + 2 - flatX1, layer_image.pixel_y + 2 - flatY1)

var/curdir
if(A.dir != 2 && !always_use_defdir)
curdir = A.dir
else
curdir = defdir
if(appearance.color)
if(islist(appearance.color))
flat.MapColors(arglist(appearance.color))
else
flat.Blend(appearance.color, ICON_MULTIPLY)

var/curblend
if(A.blend_mode == BLEND_DEFAULT)
curblend = defblend
else
curblend = A.blend_mode

// Layers will be a sorted list of icons/overlays, based on the order in which they are displayed
var/list/layers = list()
var/image/copy
// Add the atom's icon itself, without pixel_x/y offsets.
if(!noIcon)
copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=curdir)
copy.color = A.color
copy.alpha = A.alpha
copy.blend_mode = curblend
layers[copy] = A.layer
if(appearance.alpha < 255)
flat.Blend(rgb(255, 255, 255, appearance.alpha), ICON_MULTIPLY)

// Loop through the underlays, then overlays, sorting them into the layers list
var/list/process = A.underlays // Current list being processed
var/pSet=0 // Which list is being processed: 0 = underlays, 1 = overlays
var/curIndex=1 // index of 'current' in list being processed
var/current // Current overlay being sorted
var/currentLayer // Calculated layer that overlay appears on (special case for FLOAT_LAYER)
var/compare // The overlay 'add' is being compared against
var/cmpIndex // The index in the layers list of 'compare'
while(TRUE)
if(curIndex<=length(process))
current = process[curIndex]
if(current)
currentLayer = current:layer
if(currentLayer<0) // Special case for FLY_LAYER
if(currentLayer <= -1000) return flat
if(pSet == 0) // Underlay
currentLayer = A.layer+currentLayer/1000
else // Overlay
currentLayer = A.layer+(1000+currentLayer)/1000

// Sort add into layers list
for(cmpIndex=1,cmpIndex<=length(layers),cmpIndex++)
compare = layers[cmpIndex]
if(currentLayer < layers[compare]) // Associated value is the calculated layer
layers.Insert(cmpIndex,current)
layers[current] = currentLayer
break
if(cmpIndex>length(layers)) // Reached end of list without inserting
layers[current]=currentLayer // Place at end

curIndex++
else if(pSet == 0) // Switch to overlays
curIndex = 1
pSet = 1
process = A.overlays
else // All done
break

var/icon/add // Icon of overlay being added

// Current dimensions of flattened icon
var/flatX1=1
var/flatX2=flat.Width()
var/flatY1=1
var/flatY2=flat.Height()
// Dimensions of overlay being added
var/addX1
var/addX2
var/addY1
var/addY2

for(var/I in layers)

if(I:plane == EMISSIVE_PLANE) //Just replace this with whatever it is TG is doing these days sometime. Getflaticon breaks emissives
continue

if(I:alpha == 0)
continue

if(I == copy) // 'I' is an /image based on the object being flattened.
curblend = BLEND_OVERLAY
add = icon(I:icon, I:icon_state, I:dir)
else // 'I' is an appearance object.
if(istype(A,/obj/machinery/atmospherics) && (I in A.underlays))
var/image/Im = I
add = getFlatIcon(new/image(I), Im.dir, curicon, curstate, curblend, 1)
else
add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend, always_use_defdir)

// Find the new dimensions of the flat icon to fit the added overlay
addX1 = min(flatX1, I:pixel_x+1)
addX2 = max(flatX2, I:pixel_x+add.Width())
addY1 = min(flatY1, I:pixel_y+1)
addY2 = max(flatY2, I:pixel_y+add.Height())

if(addX1!=flatX1 || addX2!=flatX2 || addY1!=flatY1 || addY2!=flatY2)
// Resize the flattened icon so the new icon fits
flat.Crop(addX1-flatX1+1, addY1-flatY1+1, addX2-flatX1+1, addY2-flatY1+1)
flatX1=addX1;flatX2=addX2
flatY1=addY1;flatY2=addY2
var/iconmode
if(I in A.overlays)
iconmode = ICON_OVERLAY
else if(I in A.underlays)
iconmode = ICON_UNDERLAY
if(no_anim)
//Clean up repeated frames
var/icon/cleaned = new /icon()
cleaned.Insert(flat, "", SOUTH, 1, 0)
return cleaned
else
iconmode = blendMode2iconMode(curblend)
// Blend the overlay into the flattened icon
flat.Blend(add, iconmode, I:pixel_x + 2 - flatX1, I:pixel_y + 2 - flatY1)
return icon(flat, "", SOUTH)
else if (render_icon) // There's no overlays.
var/icon/final_icon = icon(icon(curicon, curstate, base_icon_dir), "", SOUTH, no_anim ? TRUE : null)

if(A.color)
flat.Blend(A.color, ICON_MULTIPLY)
if(A.alpha < 255)
flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY)
if (appearance.alpha < 255)
final_icon.Blend(rgb(255,255,255, appearance.alpha), ICON_MULTIPLY)

return icon(flat, "", SOUTH)
if (appearance.color)
if (islist(appearance.color))
final_icon.MapColors(arglist(appearance.color))
else
final_icon.Blend(appearance.color, ICON_MULTIPLY)

return final_icon

#undef PROCESS_OVERLAYS_OR_UNDERLAYS
//[/SIERRA-EDIT]

/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N
RETURN_TYPE(/icon)
Expand Down Expand Up @@ -646,7 +657,7 @@ The _flatIcons list is a cache for generated icon files.
for(var/i = 1; gap + i <= length(result); i++)
var/atom/l = result[i] //Fucking hate
var/atom/r = result[gap+i] //how lists work here
if(l.plane > r.plane || (l.plane == r.plane && l.layer > r.layer)) //no "result[i].layer" for me
if(l.layer > r.layer) //no "result[i].layer" for me //[SIERRA-EDIT]
result.Swap(i, gap + i)
swapped = 1
return result
Expand All @@ -656,6 +667,7 @@ arguments tx, ty, tz are target coordinates (requred), range defines render dist
cap_mode is capturing mode (optional), user is capturing mob (requred only wehen cap_mode = CAPTURE_MODE_REGULAR),
lighting determines lighting capturing (optional), suppress_errors suppreses errors and continues to capture (optional).
*/
//[SIERRA-EDIT]
/proc/generate_image(tx as num, ty as num, tz as num, range as num, cap_mode = CAPTURE_MODE_PARTIAL, mob/living/user, lighting = 1, suppress_errors = 1)
RETURN_TYPE(/icon)
var/list/turfstocapture = list()
Expand All @@ -674,17 +686,30 @@ lighting determines lighting capturing (optional), suppress_errors suppreses err
//Capture includes non-existan turfs
if(!suppress_errors)
return

return generate_image_from_turfs(locate(tx, ty, tz), turfstocapture, range, cap_mode, user, lighting)

/proc/generate_image_from_turfs(turf/topleft, list/turf/turfstocapture, range as num, cap_mode = CAPTURE_MODE_PARTIAL, mob/living/user, lighting = TRUE)
var/tx = topleft.x
var/ty = topleft.y
//Lines below determine what objects will be rendered
var/list/atoms = list()
var/list/render_lighting = list()
for(var/turf/T in turfstocapture)
atoms.Add(T)
atoms += T
for(var/atom/A in T)
if(istype(A, /atom/movable/lighting_overlay) && lighting) //Special case for lighting
atoms.Add(A)

if(istype(A, /atom/movable/lighting_overlay)) //Special case for lighting
if(lighting)
render_lighting.Add(A)
continue
if(A.invisibility) continue
atoms.Add(A)
//Lines below actually render all colected data

if(A.invisibility)
continue

atoms += A

//Lines below actually render all collected data
atoms = sort_atoms_by_layer(atoms)
var/icon/cap = icon('icons/effects/96x96.dmi', "")
cap.Scale(range*32, range*32)
Expand All @@ -699,4 +724,13 @@ lighting determines lighting capturing (optional), suppress_errors suppreses err
var/yoff = (A.y - ty) * 32
cap.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff)

if(lighting)
for(var/atom/movable/lighting_overlay/lighting_overlay as anything in render_lighting)
var/icon/lighting_overlay_icon = getFlatIcon(lighting_overlay)
var/x_offset = (lighting_overlay.x - tx) * world.icon_size
var/y_offset = (lighting_overlay.y - ty) * world.icon_size
cap.Blend(lighting_overlay_icon, ICON_MULTIPLY, lighting_overlay.pixel_x + x_offset, lighting_overlay.pixel_y + y_offset)

return cap

//[/SIERRA-EDIT]
6 changes: 4 additions & 2 deletions code/game/objects/items/weapons/cards_ids.dm
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,10 @@ var/global/const/NO_EMAG_ACT = -50

/obj/item/card/id/proc/set_id_photo(mob/M)
M.ImmediateOverlayUpdate()
front = getFlatIcon(M, SOUTH, always_use_defdir = 1)
side = getFlatIcon(M, WEST, always_use_defdir = 1)
//[SIERRA-EDIT]
front = getFlatIcon(M, SOUTH)
side = getFlatIcon(M, WEST)
//[/SIERRA-EDIT]

/mob/proc/set_id_info(obj/item/card/id/id_card)
id_card.age = 0
Expand Down
2 changes: 1 addition & 1 deletion code/modules/client/preference_setup/general/05_preview.dm
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
// last_built_icon.Blend(getFlatIcon(mannequin, NORTH, always_use_defdir = TRUE), ICON_OVERLAY, 25, 17)
// CHECK_TICK
mannequin.dir = man_dir
last_built_icon.Blend(getFlatIcon(mannequin, man_dir, always_use_defdir = TRUE), ICON_OVERLAY, 25, 3)
last_built_icon.Blend(getFlatIcon(mannequin, man_dir), ICON_OVERLAY, 25, 3) //[SIERRA-EDIT]
preview_icon = new (last_built_icon)
var/scale = client.get_preference_value(/datum/client_preference/preview_scale)
switch (scale)
Expand Down
Loading

0 comments on commit 5938853

Please sign in to comment.