mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-23 12:07:27 +01:00
[FEATURE] Decal painter (#27036)
* Add_Decal_Painter * Seperate TGUI file * Decal painter DmIcon * Fix some merge errors * I don't know why this merge is so incredibly fucked * Update TGUI bundle * I swear to god * Fixes DmIcon * TGUI bundle build * Remove implicit var * Final cleanup * Add decal_painter sprite * Update code/game/objects/items/devices/painter/decal_painter.dm Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Signed-off-by: Chap <erwin@lombok.demon.nl> * Add helper proc for deleting all components of a type from a datum * Added comments to cycle_style * Elementized decal fixes * Use the new decal system * TGUI bundle --------- Signed-off-by: Chap <erwin@lombok.demon.nl> Co-authored-by: Adrer <adrermail@gmail.com> Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
This commit is contained in:
@@ -29,3 +29,5 @@
|
||||
#define COMSIG_TURF_CHANGE "turf_change"
|
||||
///from base of turf/proc/onShuttleMove(): (turf/new_turf)
|
||||
#define COMSIG_TURF_ON_SHUTTLE_MOVE "turf_on_shuttle_move"
|
||||
///from base of turf/proc/get_decals(): (list/datum/element/decal/decals)
|
||||
#define COMSIG_ATOM_GET_DECALS "atom_get_decals"
|
||||
|
||||
@@ -449,6 +449,15 @@
|
||||
if(istype(removing, component_to_nuke) && !QDELETED(removing))
|
||||
qdel(removing)
|
||||
|
||||
/**
|
||||
* Removes all components of a given type from the datum
|
||||
*/
|
||||
/datum/proc/DeleteComponentsType(component_type_to_nuke)
|
||||
var/list/components = GetComponents(component_type_to_nuke)
|
||||
for(var/datum/component/removing in components)
|
||||
if(!QDELETED(removing))
|
||||
qdel(removing)
|
||||
|
||||
/**
|
||||
* Get existing component of type, or create it and return a reference to it
|
||||
*
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
if(isitem(target))
|
||||
INVOKE_ASYNC(target, TYPE_PROC_REF(/obj/item/, update_slot_icon), TRUE)
|
||||
if(_dir)
|
||||
RegisterSignal(target, COMSIG_ATOM_DECALS_ROTATING, PROC_REF(shuttle_rotate), TRUE)
|
||||
RegisterSignal(target, list(COMSIG_ATOM_DECALS_ROTATING, COMSIG_ATOM_GET_DECALS), PROC_REF(get_decals), TRUE)
|
||||
SSdcs.RegisterSignal(target, COMSIG_ATOM_DIR_CHANGE, TYPE_PROC_REF(/datum/controller/subsystem/processing/dcs, rotate_decals), override=TRUE)
|
||||
if(_cleanable)
|
||||
RegisterSignal(target, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean_react), TRUE)
|
||||
@@ -172,6 +172,6 @@
|
||||
Detach(source)
|
||||
new_turf.AddElement(type, pic.icon, base_icon_state, directional, pic.layer, pic.alpha, pic.color, cleanable, description)
|
||||
|
||||
/datum/element/decal/proc/shuttle_rotate(datum/source, list/datum/element/decal/rotating)
|
||||
/datum/element/decal/proc/get_decals(datum/source, list/datum/element/decal/rotating)
|
||||
SIGNAL_HANDLER // COMSIG_ATOM_DECALS_ROTATING
|
||||
rotating += src
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
icon_state = "warningline"
|
||||
layer = TURF_DECAL_LAYER
|
||||
|
||||
/obj/effect/turf_decal/Initialize(mapload)
|
||||
/obj/effect/turf_decal/Initialize(mapload, _dir)
|
||||
..()
|
||||
. = INITIALIZE_HINT_QDEL
|
||||
var/turf/T = loc
|
||||
if(!istype(T)) //you know this will happen somehow
|
||||
CRASH("Turf decal initialized in an object/nullspace")
|
||||
|
||||
T.AddElement(/datum/element/decal, icon, icon_state, dir, layer, alpha, color, FALSE, null)
|
||||
T.AddElement(/datum/element/decal, icon, icon_state, _dir || dir, layer, alpha, color, FALSE, null)
|
||||
|
||||
@@ -199,9 +199,6 @@
|
||||
/obj/effect/turf_decal/siding/box
|
||||
icon_state = "siding_box"
|
||||
|
||||
/obj/effect/turf_decal/raven_ship_sign
|
||||
icon_state = "RAVEN2"
|
||||
|
||||
/obj/effect/turf_decal/raven/one
|
||||
icon_state = "RAVEN1"
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
name = "sandy floor"
|
||||
icon_state = "sandyfloor"
|
||||
|
||||
/obj/effect/turf_decal/weather/snow
|
||||
name = "snowy floor"
|
||||
icon = 'icons/turf/snow.dmi'
|
||||
icon_state = "snow"
|
||||
|
||||
/obj/effect/turf_decal/weather/snow/corner
|
||||
name = "snow corner piece"
|
||||
icon = 'icons/turf/snow.dmi'
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/datum/painter/decal
|
||||
module_name = "decal painter"
|
||||
module_state = "decal_painter"
|
||||
/// icon that contains the decal sprites
|
||||
var/decal_icon = 'icons/turf/decals.dmi'
|
||||
/// icon_state of the selected decal
|
||||
var/decal_state = "warn_box"
|
||||
var/decal_dir = SOUTH
|
||||
/// When removal_mode is TRUE the decal painter will remove decals instead
|
||||
var/removal_mode = FALSE
|
||||
var/max_decals = 3
|
||||
var/static/list/decal_blacklist = typecacheof(
|
||||
list(
|
||||
/obj/effect/turf_decal/raven,
|
||||
/obj/effect/turf_decal/weather,
|
||||
/obj/effect/turf_decal/stripes/asteroid,
|
||||
/obj/effect/turf_decal/tile,
|
||||
/obj/effect/turf_decal/sand
|
||||
)
|
||||
)
|
||||
/// Assoc list with icon_state of the decal as the key, and decal path as the value.
|
||||
var/static/list/lookup_cache_decals = list()
|
||||
|
||||
/datum/painter/decal/New(obj/item/painter/parent_painter)
|
||||
. = ..()
|
||||
if(!length(lookup_cache_decals))
|
||||
for(var/D in subtypesof(/obj/effect/turf_decal))
|
||||
var/obj/effect/turf_decal/decal = D
|
||||
if(decal in decal_blacklist)
|
||||
continue
|
||||
lookup_cache_decals[decal::icon_state] = decal
|
||||
|
||||
/datum/painter/decal/paint_atom(atom/target, mob/user)
|
||||
if(!istype(target, /turf/simulated/floor))
|
||||
to_chat(user, "<span class='warning'>[holder] can only be used on flooring.</span>")
|
||||
return FALSE
|
||||
var/turf/target_turf = get_turf(target)
|
||||
var/list/datum/element/decal/decals = target_turf.get_decals()
|
||||
if(removal_mode)
|
||||
remove_decals(target)
|
||||
return TRUE
|
||||
if(length(decals) >= max_decals)
|
||||
to_chat(user, "<span class='warning'>You can't fit more decals on [target].</span>")
|
||||
return FALSE
|
||||
var/typepath = lookup_cache_decals[decal_state]
|
||||
new typepath(target_turf, decal_dir)
|
||||
return TRUE
|
||||
|
||||
/datum/painter/decal/pick_color(mob/user)
|
||||
if(!user)
|
||||
return
|
||||
ui_interact(user)
|
||||
|
||||
/datum/painter/decal/ui_state(mob/user)
|
||||
return GLOB.inventory_state
|
||||
|
||||
/datum/painter/decal/ui_interact(mob/user, datum/tgui/ui = null)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "DecalPainter", module_name)
|
||||
ui.set_autoupdate(FALSE)
|
||||
ui.open()
|
||||
|
||||
/datum/painter/decal/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["selectedStyle"] = decal_state
|
||||
data["selectedDir"] = decal_dir
|
||||
data["removalMode"] = removal_mode
|
||||
|
||||
return data
|
||||
|
||||
|
||||
/datum/painter/decal/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["icon"] = decal_icon
|
||||
data["availableStyles"] = list()
|
||||
for(var/decal in lookup_cache_decals)
|
||||
data["availableStyles"] += decal
|
||||
|
||||
return data
|
||||
|
||||
/datum/painter/decal/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if(action == "select_style")
|
||||
var/new_style = params["style"]
|
||||
if(lookup_cache_decals.Find(new_style) != 0)
|
||||
decal_state = new_style
|
||||
removal_mode = FALSE
|
||||
|
||||
if(action == "cycle_style") // Cycles through the available styles one at a time
|
||||
var/index = lookup_cache_decals.Find(decal_state) // Find the index of the currently selected style in the lookup cache
|
||||
index += params["offset"] // Offset is either -1 or 1. Add this to the index to get the style before or after the current style.
|
||||
if(index < 1) // If the index is below 1, loop back to the last item in the cache.
|
||||
index = length(lookup_cache_decals)
|
||||
if(index > length(lookup_cache_decals)) // If the index is above the length of the cache, loop back to the first item in the cache.
|
||||
index = 1
|
||||
decal_state = lookup_cache_decals[index] // Then set our state to the index
|
||||
removal_mode = FALSE
|
||||
|
||||
if(action == "select_direction")
|
||||
var/dir = params["direction"]
|
||||
removal_mode = FALSE
|
||||
if(dir != 0)
|
||||
decal_dir = dir
|
||||
|
||||
if(action == "removal_mode")
|
||||
removal_mode = !removal_mode
|
||||
return TRUE
|
||||
|
||||
/datum/painter/decal/proc/remove_decals(atom/target)
|
||||
var/turf/target_turf = get_turf(target)
|
||||
var/list/datum/element/decal/decals = target_turf.get_decals()
|
||||
for(var/datum/element/decal/dcl in decals)
|
||||
dcl.Detach(target)
|
||||
target_turf.RemoveElement(/datum/element/decal)
|
||||
@@ -12,14 +12,16 @@
|
||||
"Floor Painter" = /datum/painter/floor,
|
||||
"Pipe Painter" = /datum/painter/pipe,
|
||||
"Window Painter" = /datum/painter/pipe/window,
|
||||
"Airlock Painter" = /datum/painter/airlock)
|
||||
"Airlock Painter" = /datum/painter/airlock,
|
||||
"Decal Painter" = /datum/painter/decal)
|
||||
|
||||
/// Associative list of painter types, with the value being the icon. (For use in the radial menu)
|
||||
var/static/list/painter_icon_list = list(
|
||||
"Floor Painter" = image(icon = 'icons/obj/painting.dmi', icon_state = "floor_painter"),
|
||||
"Pipe Painter" = image(icon = 'icons/obj/painting.dmi', icon_state = "pipe_painter"),
|
||||
"Window Painter" = image(icon = 'icons/obj/painting.dmi', icon_state = "window_painter"),
|
||||
"Airlock Painter" = image(icon = 'icons/obj/painting.dmi', icon_state = "airlock_painter"))
|
||||
"Airlock Painter" = image(icon = 'icons/obj/painting.dmi', icon_state = "airlock_painter"),
|
||||
"Decal Painter" = image(icon = 'icons/obj/painting.dmi', icon_state = "decal_painter"))
|
||||
|
||||
/// The [/datum/painter] which is currently active.
|
||||
var/datum/painter/selected_module = null
|
||||
|
||||
@@ -603,6 +603,13 @@
|
||||
C.take_organ_damage(damage)
|
||||
C.KnockDown(3 SECONDS)
|
||||
|
||||
/// Returns a list of all attached /datum/element/decal/ for this turf
|
||||
/turf/proc/get_decals()
|
||||
var/list/datum/element/decals = list()
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_GET_DECALS, decals)
|
||||
|
||||
return decals
|
||||
|
||||
/turf/proc/initialize_milla()
|
||||
var/datum/milla_safe/initialize_turf/milla = new()
|
||||
milla.invoke_async(src)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 3.1 KiB |
@@ -1110,6 +1110,7 @@
|
||||
#include "code\game\objects\items\devices\voice_changer.dm"
|
||||
#include "code\game\objects\items\devices\whistle.dm"
|
||||
#include "code\game\objects\items\devices\painter\airlock_painter.dm"
|
||||
#include "code\game\objects\items\devices\painter\decal_painter.dm"
|
||||
#include "code\game\objects\items\devices\painter\floor_painter.dm"
|
||||
#include "code\game\objects\items\devices\painter\painter.dm"
|
||||
#include "code\game\objects\items\devices\painter\pipe_painter.dm"
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Button, LabeledList, Section, Table, Dropdown, Flex, Icon, Box, DmIcon } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
const SelectableTile = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { icon_state, direction, isSelected, onSelect } = props;
|
||||
|
||||
return (
|
||||
<DmIcon
|
||||
icon={data.icon}
|
||||
icon_state={icon_state}
|
||||
direction={direction}
|
||||
onClick={onSelect}
|
||||
style={{
|
||||
'border-style': (isSelected && 'solid') || 'none',
|
||||
'border-width': '2px',
|
||||
'border-color': 'orange',
|
||||
padding: (isSelected && '0px') || '2px',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Dir = {
|
||||
NORTH: 1,
|
||||
SOUTH: 2,
|
||||
EAST: 4,
|
||||
WEST: 8,
|
||||
};
|
||||
|
||||
export const DecalPainter = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { availableStyles, selectedStyle, selectedDir, removalMode } = data;
|
||||
return (
|
||||
<Window width={405} height={475}>
|
||||
<Window.Content scrollable>
|
||||
<Section title="Decal setup">
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Button icon="chevron-left" onClick={() => act('cycle_style', { offset: -1 })} />
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Dropdown
|
||||
options={availableStyles}
|
||||
selected={selectedStyle}
|
||||
width="150px"
|
||||
height="20px"
|
||||
ml="2px"
|
||||
mr="2px"
|
||||
nochevron
|
||||
onSelected={(val) => act('select_style', { style: val })}
|
||||
/>
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Button icon="chevron-right" onClick={() => act('cycle_style', { offset: 1 })} />
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Button icon="eraser" color={removalMode ? 'green' : 'transparent'} onClick={() => act('removal_mode')}>
|
||||
Remove decals
|
||||
</Button>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
|
||||
<Box mt="5px" mb="5px">
|
||||
<Flex
|
||||
overflowY="auto" // scroll
|
||||
maxHeight="220px" // a bit more than half of all tiles fit in this box at once.
|
||||
wrap="wrap"
|
||||
>
|
||||
{availableStyles.map((style) => (
|
||||
<Flex.Item key={style}>
|
||||
<SelectableTile
|
||||
icon_state={style}
|
||||
isSelected={selectedStyle === style && !removalMode}
|
||||
onSelect={() => act('select_style', { style: style })}
|
||||
/>
|
||||
</Flex.Item>
|
||||
))}
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Direction">
|
||||
<Table style={{ display: 'inline' }}>
|
||||
{[Dir.NORTH, null, Dir.SOUTH].map((latitude) => (
|
||||
<Table.Row key={latitude}>
|
||||
{[latitude + Dir.WEST, latitude, latitude + Dir.EAST].map((dir) => (
|
||||
<Table.Cell
|
||||
key={dir}
|
||||
style={{
|
||||
'vertical-align': 'middle',
|
||||
'text-align': 'center',
|
||||
}}
|
||||
>
|
||||
{dir === null ? (
|
||||
<Icon name="arrows-alt" size={3} />
|
||||
) : (
|
||||
<SelectableTile
|
||||
icon_state={selectedStyle}
|
||||
direction={dir}
|
||||
isSelected={dir === selectedDir && !removalMode}
|
||||
onSelect={() => act('select_direction', { direction: dir })}
|
||||
/>
|
||||
)}
|
||||
</Table.Cell>
|
||||
))}
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
+45
-45
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user