Skip to content

Commit

Permalink
dir lighting
Browse files Browse the repository at this point in the history
  • Loading branch information
harryob committed Aug 24, 2023
1 parent 7f5e2bc commit 52a089e
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 21 deletions.
4 changes: 4 additions & 0 deletions code/__DEFINES/lighting.dm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
#define MOVABLE_LIGHT 2
///A mix of the above, cheaper on moving items than dynamic, but heavier on rendering than movable
#define HYBRID_LIGHT 3
///Pointy light
#define DIRECTIONAL_LIGHT 4

#define LIGHT_ATTACHED (1<<0)

#define MINIMUM_USEFUL_LIGHT_RANGE 1.4

Expand Down
233 changes: 215 additions & 18 deletions code/datums/components/overlay_lighting.dm
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
///For switchable lights, is it on and currently emitting light?
#define LIGHTING_ON (1<<0)
///Is the parent attached to something else, its loc? Then we need to keep an eye of this.
#define LIGHTING_ATTACHED (1<<1)

#define GET_PARENT (parent_attached_to || parent)

#define SHORT_CAST 2


/**
* Movable atom overlay-based lighting component.
Expand Down Expand Up @@ -50,15 +57,29 @@
var/list/turf/affected_turfs
///Movable atom currently holding the light. Parent might be a flashlight, for example, but that might be held by a mob or something else.
var/atom/movable/current_holder


/datum/component/overlay_lighting/Initialize(_range, _power, _color, starts_on)
///Movable atom the parent is attached to. For example, a flashlight into a helmet or gun. We'll need to track the thing the parent is attached to as if it were the parent itself.
var/atom/movable/parent_attached_to
///Whether we're a directional light
var/directional = FALSE
///A cone overlay for directional light, it's alpha and color are dependant on the light
var/image/cone
///Current tracked direction for the directional cast behaviour
var/current_direction
///Tracks current directional x offset so we dont update unecessarily
var/directional_offset_x
///Tracks current directional y offset so we dont update unecessarily
var/directional_offset_y
///Cast range for the directional cast (how far away the atom is moved)
var/cast_range = 2


/datum/component/overlay_lighting/Initialize(_range, _power, _color, starts_on, is_directional)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE

var/atom/movable/movable_parent = parent
if(movable_parent.light_system != MOVABLE_LIGHT)
stack_trace("[type] added to [parent], with [movable_parent.light_system] value for the light_system var. Use [MOVABLE_LIGHT] instead.")
if(movable_parent.light_system != MOVABLE_LIGHT && movable_parent.light_system != DIRECTIONAL_LIGHT)
stack_trace("[type] added to [parent], with [movable_parent.light_system] value for the light_system var. Use [MOVABLE_LIGHT]/[DIRECTIONAL_LIGHT] instead.")
return COMPONENT_INCOMPATIBLE

. = ..()
Expand All @@ -67,6 +88,14 @@
visible_mask.plane = O_LIGHTING_VISUAL_PLANE
visible_mask.appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM
visible_mask.alpha = 0
if(is_directional)
directional = TRUE
cone = image('icons/effects/light_overlays/light_cone.dmi', icon_state = "light")
cone.plane = O_LIGHTING_VISUAL_PLANE
cone.appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM
cone.alpha = 110
cone.transform = cone.transform.Translate(-32, -32)
set_direction(movable_parent.dir)
if(!isnull(_range))
movable_parent.set_light_range(_range)
set_range(parent, movable_parent.light_range)
Expand All @@ -81,19 +110,28 @@

/datum/component/overlay_lighting/RegisterWithParent()
. = ..()
if(directional)
RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_parent_dir_change))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_parent_moved))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(check_holder))
RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_RANGE, PROC_REF(set_range))
RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_POWER, PROC_REF(set_power))
RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_COLOR, PROC_REF(set_color))
RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_ON, PROC_REF(on_toggle))
RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_FLAGS, PROC_REF(on_light_flags_change))
var/atom/movable/movable_parent = parent
if(movable_parent.light_flags & LIGHT_ATTACHED)
overlay_lighting_flags |= LIGHTING_ATTACHED
set_parent_attached_to(ismovable(movable_parent.loc) ? movable_parent.loc : null)
check_holder()
if(movable_parent.light_on)
turn_on()
INVOKE_NEXT_TICK(src, PROC_REF(check_holder))



/datum/component/overlay_lighting/UnregisterFromParent()
overlay_lighting_flags &= ~LIGHTING_ATTACHED
set_parent_attached_to(null)
set_holder(null)
clean_old_turfs()
UnregisterSignal(parent, list(
Expand All @@ -104,15 +142,20 @@
COMSIG_ATOM_SET_LIGHT_ON,
COMSIG_ATOM_SET_LIGHT_FLAGS,
))
if(directional)
UnregisterSignal(parent, COMSIG_ATOM_DIR_CHANGE)
if(overlay_lighting_flags & LIGHTING_ON)
turn_off()
return ..()


/datum/component/overlay_lighting/Destroy()
set_parent_attached_to(null)
set_holder(null)
clean_old_turfs()
visible_mask = null
cone = null
parent_attached_to = null
return ..()


Expand Down Expand Up @@ -144,41 +187,77 @@
clean_old_turfs()
if(!isturf(current_holder?.loc))
return
if(directional)
cast_directional_light()
get_new_turfs()


///Adds the luminosity and source for the afected movable atoms to keep track of their visibility.
/datum/component/overlay_lighting/proc/add_dynamic_lumi(atom/movable/affected_movable)
LAZYSET(affected_movable.affected_movable_lights, src, lumcount_range + 1)
affected_movable.underlays += visible_mask
affected_movable.update_dynamic_luminosity()

/datum/component/overlay_lighting/proc/add_dynamic_lumi()
LAZYSET(current_holder.affected_movable_lights, src, lumcount_range + 1)
current_holder.underlays += visible_mask
current_holder.update_dynamic_luminosity()
if(directional)
current_holder.underlays += cone

///Removes the luminosity and source for the afected movable atoms to keep track of their visibility.
/datum/component/overlay_lighting/proc/remove_dynamic_lumi(atom/movable/affected_movable)
LAZYREMOVE(affected_movable.affected_movable_lights, src)
affected_movable.underlays -= visible_mask
affected_movable.update_dynamic_luminosity()
/datum/component/overlay_lighting/proc/remove_dynamic_lumi()
LAZYREMOVE(current_holder.affected_movable_lights, src)
current_holder.underlays -= visible_mask
current_holder.update_dynamic_luminosity()
if(directional)
current_holder.underlays -= cone

///Called to change the value of parent_attached_to.
/datum/component/overlay_lighting/proc/set_parent_attached_to(atom/movable/new_parent_attached_to)
if(new_parent_attached_to == parent_attached_to)
return

. = parent_attached_to
parent_attached_to = new_parent_attached_to
if(.)
var/atom/movable/old_parent_attached_to = .
UnregisterSignal(old_parent_attached_to, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED))
if(old_parent_attached_to == current_holder)
RegisterSignal(old_parent_attached_to, COMSIG_PARENT_QDELETING, PROC_REF(on_holder_qdel))
RegisterSignal(old_parent_attached_to, COMSIG_MOVABLE_MOVED, PROC_REF(on_holder_moved))
if(parent_attached_to)
if(parent_attached_to == current_holder)
UnregisterSignal(current_holder, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED))
RegisterSignal(parent_attached_to, COMSIG_PARENT_QDELETING, PROC_REF(on_parent_attached_to_qdel))
RegisterSignal(parent_attached_to, COMSIG_MOVABLE_MOVED, PROC_REF(on_parent_attached_to_moved))
check_holder()


///Called to change the value of current_holder.
/datum/component/overlay_lighting/proc/set_holder(atom/movable/new_holder)
if(new_holder == current_holder)
return
if(current_holder)
if(current_holder != parent && current_holder != parent_attached_to)
UnregisterSignal(current_holder, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED))
if(directional)
UnregisterSignal(current_holder, COMSIG_ATOM_DIR_CHANGE)
if(overlay_lighting_flags & LIGHTING_ON)
remove_dynamic_lumi(current_holder)
current_holder = new_holder
if(new_holder == null)
clean_old_turfs()
return
if(new_holder != parent)
RegisterSignal(new_holder, COMSIG_PARENT_QDELETING, PROC_REF(on_holder_qdel))
if(overlay_lighting_flags & LIGHTING_ON)
RegisterSignal(new_holder, COMSIG_MOVABLE_MOVED, PROC_REF(on_holder_moved))
if(directional)
RegisterSignal(new_holder, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_holder_dir_change))
set_direction(new_holder.dir)
if(overlay_lighting_flags & LIGHTING_ON)
add_dynamic_lumi(new_holder)


///Used to determine the new valid current_holder from the parent's loc.
/datum/component/overlay_lighting/proc/check_holder()
var/atom/movable/movable_parent = parent
var/atom/movable/movable_parent = GET_PARENT
if(isturf(movable_parent.loc))
set_holder(movable_parent)
return
Expand All @@ -204,10 +283,30 @@
return
make_luminosity_update()

///Called when the current_holder is qdeleted, to remove the light effect.
/datum/component/overlay_lighting/proc/on_parent_attached_to_qdel(atom/movable/source, force)
SIGNAL_HANDLER
UnregisterSignal(parent_attached_to, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED))
if(directional)
UnregisterSignal(parent_attached_to, COMSIG_ATOM_DIR_CHANGE)
if(parent_attached_to == current_holder)
set_holder(null)
set_parent_attached_to(null)

///Called when parent_attached_to changes loc.
/datum/component/overlay_lighting/proc/on_parent_attached_to_moved(atom/movable/source, OldLoc, Dir, Forced)
SIGNAL_HANDLER
check_holder()
if(!(overlay_lighting_flags & LIGHTING_ON) || !current_holder)
return
make_luminosity_update()

///Called when parent changes loc.
/datum/component/overlay_lighting/proc/on_parent_moved(atom/movable/source, OldLoc, Dir, Forced)
SIGNAL_HANDLER
var/atom/movable/movable_parent = parent
if(overlay_lighting_flags & LIGHTING_ATTACHED)
set_parent_attached_to(ismovable(movable_parent.loc) ? movable_parent.loc : null)
check_holder()
if(!(overlay_lighting_flags & LIGHTING_ON) || !current_holder)
return
Expand All @@ -234,8 +333,12 @@
var/matrix/transform = new
transform.Translate(-offset, -offset)
visible_mask.transform = transform
directional_offset_x = 0
directional_offset_y = 0
if(current_holder && overlay_lighting_flags & LIGHTING_ON)
current_holder.underlays += visible_mask
if(directional)
cast_range = clamp(round(new_range * 0.5), 1, 3)
if(overlay_lighting_flags & LIGHTING_ON)
make_luminosity_update()

Expand All @@ -251,6 +354,15 @@
if(current_holder && overlay_lighting_flags & LIGHTING_ON)
current_holder.underlays += visible_mask

if(!directional)
return

if(current_holder && overlay_lighting_flags & LIGHTING_ON)
current_holder.underlays -= cone
cone.alpha = min(200, (abs(new_power) * 90)+20)
if(current_holder && overlay_lighting_flags & LIGHTING_ON)
current_holder.underlays += cone


///Changes the light's color, pretty straightforward.
/datum/component/overlay_lighting/proc/set_color(atom/source, new_color)
Expand All @@ -261,6 +373,15 @@
if(current_holder && overlay_lighting_flags & LIGHTING_ON)
current_holder.underlays += visible_mask

if(!directional)
return

if(current_holder && overlay_lighting_flags & LIGHTING_ON)
current_holder.underlays -= cone
cone.color = new_color
if(current_holder && overlay_lighting_flags & LIGHTING_ON)
current_holder.underlays += cone


///Toggles the light on and off.
/datum/component/overlay_lighting/proc/on_toggle(atom/source, new_value)
Expand All @@ -270,14 +391,30 @@
return
turn_off() //Falsey value, turn off.

///Triggered right after the parent light flags change.
/datum/component/overlay_lighting/proc/on_light_flags_change(atom/source, new_flags)
SIGNAL_HANDLER
var/atom/movable/movable_parent = parent

if(new_flags & LIGHT_ATTACHED) // Gained the [LIGHT_ATTACHED] property
overlay_lighting_flags |= LIGHTING_ATTACHED
if(ismovable(movable_parent.loc))
set_parent_attached_to(movable_parent.loc)
else // Lost the [LIGHT_ATTACHED] property
overlay_lighting_flags &= ~LIGHTING_ATTACHED
set_parent_attached_to(null)

///Toggles the light on.
/datum/component/overlay_lighting/proc/turn_on()
if(overlay_lighting_flags & LIGHTING_ON)
return
overlay_lighting_flags |= LIGHTING_ON
if(current_holder)
add_dynamic_lumi(current_holder)
overlay_lighting_flags |= LIGHTING_ON
if(directional)
cast_directional_light()
if(current_holder && current_holder != parent && current_holder != parent_attached_to)
RegisterSignal(current_holder, COMSIG_MOVABLE_MOVED, PROC_REF(on_holder_moved))
get_new_turfs()


Expand All @@ -286,8 +423,10 @@
if(!(overlay_lighting_flags & LIGHTING_ON))
return
if(current_holder)
remove_dynamic_lumi(current_holder)
remove_dynamic_lumi()
overlay_lighting_flags &= ~LIGHTING_ON
if(current_holder && current_holder != parent && current_holder != parent_attached_to)
UnregisterSignal(current_holder, COMSIG_MOVABLE_MOVED)
clean_old_turfs()


Expand All @@ -302,5 +441,63 @@
var/turf/lit_turf = t
lit_turf.dynamic_lumcount -= difference

///Here we append the behavior associated to changing lum_power.
/datum/component/overlay_lighting/proc/cast_directional_light()
var/final_distance = cast_range
//Lower the distance by 1 if we're not looking at a cardinal direction, and we're not a short cast
if(final_distance > SHORT_CAST && !(ALL_CARDINALS & current_direction))
final_distance -= 1
var/turf/scanning = get_turf(current_holder)
for(var/i in 1 to final_distance)
var/turf/next_turf = get_step(scanning, current_direction)
if(isnull(next_turf) || IS_OPAQUE_TURF(next_turf))
final_distance = i
break
scanning = next_turf

current_holder.underlays -= visible_mask

var/translate_x = -((range - 1) * 32)
var/translate_y = translate_x
switch(current_direction)
if(NORTH)
translate_y += 32 * final_distance
if(SOUTH)
translate_y += -32 * final_distance
if(EAST)
translate_x += 32 * final_distance
if(WEST)
translate_x += -32 * final_distance
if((directional_offset_x != translate_x) || (directional_offset_y != translate_y))
directional_offset_x = translate_x
directional_offset_y = translate_y
var/matrix/transform = matrix()
transform.Translate(translate_x, translate_y)
visible_mask.transform = transform
if(overlay_lighting_flags & LIGHTING_ON)
current_holder.underlays += visible_mask

///Called when current_holder changes loc.
/datum/component/overlay_lighting/proc/on_holder_dir_change(atom/movable/source, olddir, newdir)
SIGNAL_HANDLER
set_direction(newdir)

///Called when parent changes loc.
/datum/component/overlay_lighting/proc/on_parent_dir_change(atom/movable/source, olddir, newdir)
SIGNAL_HANDLER
set_direction(newdir)

///Sets a new direction for the directional cast, then updates luminosity
/datum/component/overlay_lighting/proc/set_direction(newdir)
if(!newdir)
return
if(current_direction == newdir)
return
current_direction = newdir
if(overlay_lighting_flags & LIGHTING_ON)
make_luminosity_update()

#undef LIGHTING_ON
#undef LIGHTING_ATTACHED
#undef GET_PARENT
#undef SHORT_CAST
Loading

0 comments on commit 52a089e

Please sign in to comment.