Merge branch 'master' into donoritemmodularization
This commit is contained in:
@@ -34,7 +34,7 @@ Stands have a lot of procs which mimic mob procs. Rather than inserting hooks fo
|
||||
* Lazy associated list of type -> component/list of components.
|
||||
1. `/datum/component/var/enabled` (protected, boolean)
|
||||
* If the component is enabled. If not, it will not react to signals
|
||||
* `TRUE` by default
|
||||
* `FALSE` by default, set to `TRUE` when a signal is registered
|
||||
1. `/datum/component/var/dupe_mode` (protected, enum)
|
||||
* How duplicate component types are handled when added to the datum.
|
||||
* `COMPONENT_DUPE_HIGHLANDER` (default): Old component will be deleted, new component will first have `/datum/component/proc/InheritComponent(datum/component/old, FALSE)` on it
|
||||
@@ -78,6 +78,7 @@ Stands have a lot of procs which mimic mob procs. Rather than inserting hooks fo
|
||||
1. `/datum/proc/SendSignal(signal, ...)` (public, final)
|
||||
* Call to send a signal to the components of the target datum
|
||||
* Extra arguments are to be specified in the signal definition
|
||||
* Returns a bitflag with signal specific information assembled from all activated components
|
||||
1. `/datum/component/New(datum/parent, ...)` (private, final)
|
||||
* Runs internal setup for the component
|
||||
* Extra arguments are passed to `Initialize()`
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/component
|
||||
var/enabled = TRUE
|
||||
var/enabled = FALSE
|
||||
var/dupe_mode = COMPONENT_DUPE_HIGHLANDER
|
||||
var/dupe_type
|
||||
var/list/signal_procs
|
||||
@@ -10,13 +10,21 @@
|
||||
qdel(src)
|
||||
CRASH("[type] instantiated!")
|
||||
|
||||
//check for common mishaps
|
||||
if(!isnum(dupe_mode))
|
||||
qdel(src)
|
||||
CRASH("[type]: Invalid dupe_mode!")
|
||||
if(dupe_type && !ispath(dupe_type))
|
||||
qdel(src)
|
||||
CRASH("[type]: Invalid dupe_type!")
|
||||
|
||||
parent = P
|
||||
var/list/arguments = args.Copy()
|
||||
arguments.Cut(1, 2)
|
||||
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
|
||||
qdel(src, TRUE, TRUE)
|
||||
return
|
||||
|
||||
|
||||
_CheckDupesAndJoinParent(P)
|
||||
|
||||
/datum/component/proc/_CheckDupesAndJoinParent()
|
||||
@@ -45,12 +53,12 @@
|
||||
if(!old)
|
||||
//let the others know
|
||||
P.SendSignal(COMSIG_COMPONENT_ADDED, src)
|
||||
|
||||
|
||||
//lazy init the parent's dc list
|
||||
var/list/dc = P.datum_components
|
||||
if(!dc)
|
||||
P.datum_components = dc = list()
|
||||
|
||||
|
||||
//set up the typecache
|
||||
var/our_type = type
|
||||
for(var/I in _GetInverseTypeList(our_type))
|
||||
@@ -114,7 +122,7 @@
|
||||
if(!procs)
|
||||
procs = list()
|
||||
signal_procs = procs
|
||||
|
||||
|
||||
var/list/sig_types = islist(sig_type_or_types) ? sig_type_or_types : list(sig_type_or_types)
|
||||
for(var/sig_type in sig_types)
|
||||
if(!override)
|
||||
@@ -125,6 +133,8 @@
|
||||
if(!istype(proc_or_callback, /datum/callback)) //if it wasnt a callback before, it is now
|
||||
proc_or_callback = CALLBACK(src, proc_or_callback)
|
||||
procs[sig_type] = proc_or_callback
|
||||
|
||||
enabled = TRUE
|
||||
|
||||
/datum/component/proc/InheritComponent(datum/component/C, i_am_original)
|
||||
return
|
||||
@@ -156,36 +166,35 @@
|
||||
/datum/proc/SendSignal(sigtype, ...)
|
||||
var/list/comps = datum_components
|
||||
if(!comps)
|
||||
return FALSE
|
||||
return NONE
|
||||
var/list/arguments = args.Copy()
|
||||
arguments.Cut(1, 2)
|
||||
var/target = comps[/datum/component]
|
||||
if(!length(target))
|
||||
var/datum/component/C = target
|
||||
if(!C.enabled)
|
||||
return FALSE
|
||||
var/list/sps = C.signal_procs
|
||||
var/datum/callback/CB = LAZYACCESS(sps, sigtype)
|
||||
return NONE
|
||||
var/datum/callback/CB = C.signal_procs[sigtype]
|
||||
if(!CB)
|
||||
return FALSE
|
||||
return NONE
|
||||
. = CB.InvokeAsync(arglist(arguments))
|
||||
if(.)
|
||||
if(. & COMPONENT_ACTIVATED)
|
||||
ComponentActivated(C)
|
||||
C.AfterComponentActivated()
|
||||
else
|
||||
. = FALSE
|
||||
. = NONE
|
||||
for(var/I in target)
|
||||
var/datum/component/C = I
|
||||
if(!C.enabled)
|
||||
continue
|
||||
var/list/sps = C.signal_procs
|
||||
var/datum/callback/CB = LAZYACCESS(sps, sigtype)
|
||||
continue
|
||||
var/datum/callback/CB = C.signal_procs[sigtype]
|
||||
if(!CB)
|
||||
continue
|
||||
if(CB.InvokeAsync(arglist(arguments)))
|
||||
var/retval = CB.InvokeAsync(arglist(arguments))
|
||||
. |= retval
|
||||
if(retval & COMPONENT_ACTIVATED)
|
||||
ComponentActivated(C)
|
||||
C.AfterComponentActivated()
|
||||
. = TRUE
|
||||
|
||||
/datum/proc/ComponentActivated(datum/component/C)
|
||||
set waitfor = FALSE
|
||||
@@ -255,3 +264,6 @@
|
||||
target.TakeComponent(I)
|
||||
else
|
||||
target.TakeComponent(comps)
|
||||
|
||||
/datum/component/ui_host()
|
||||
return parent
|
||||
|
||||
@@ -20,27 +20,26 @@
|
||||
/datum/component/archaeology/proc/Dig(obj/item/W, mob/living/user)
|
||||
if(dug)
|
||||
to_chat(user, "<span class='notice'>Looks like someone has dug here already.</span>")
|
||||
return FALSE
|
||||
else
|
||||
var/digging_speed
|
||||
if (istype(W, /obj/item/shovel))
|
||||
var/obj/item/shovel/S = W
|
||||
digging_speed = S.digspeed
|
||||
else if (istype(W, /obj/item/pickaxe))
|
||||
var/obj/item/pickaxe/P = W
|
||||
digging_speed = P.digspeed
|
||||
return
|
||||
|
||||
var/digging_speed
|
||||
if (istype(W, /obj/item/shovel))
|
||||
var/obj/item/shovel/S = W
|
||||
digging_speed = S.digspeed
|
||||
else if (istype(W, /obj/item/pickaxe))
|
||||
var/obj/item/pickaxe/P = W
|
||||
digging_speed = P.digspeed
|
||||
|
||||
if (digging_speed && isturf(user.loc))
|
||||
to_chat(user, "<span class='notice'>You start digging...</span>")
|
||||
playsound(parent, 'sound/effects/shovel_dig.ogg', 50, 1)
|
||||
|
||||
if (digging_speed && isturf(user.loc))
|
||||
to_chat(user, "<span class='notice'>You start digging...</span>")
|
||||
playsound(parent, 'sound/effects/shovel_dig.ogg', 50, 1)
|
||||
|
||||
if(do_after(user, digging_speed, target = parent))
|
||||
to_chat(user, "<span class='notice'>You dig a hole.</span>")
|
||||
gets_dug()
|
||||
dug = TRUE
|
||||
SSblackbox.add_details("pick_used_mining",W.type)
|
||||
return TRUE
|
||||
return FALSE
|
||||
if(do_after(user, digging_speed, target = parent))
|
||||
to_chat(user, "<span class='notice'>You dig a hole.</span>")
|
||||
gets_dug()
|
||||
dug = TRUE
|
||||
SSblackbox.record_feedback("tally", "pick_used_mining", 1, W.type)
|
||||
return COMPONENT_NO_AFTERATTACK
|
||||
|
||||
/datum/component/archaeology/proc/gets_dug()
|
||||
if(dug)
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
if(istype(victim))
|
||||
for(var/datum/disease/D in diseases)
|
||||
victim.ContactContractDisease(D, "feet")
|
||||
return TRUE
|
||||
return COMPONENT_ACTIVATED
|
||||
@@ -53,11 +53,11 @@
|
||||
/datum/component/material_container/proc/OnAttackBy(obj/item/I, mob/living/user)
|
||||
var/list/tc = allowed_typecache
|
||||
if(user.a_intent == INTENT_HARM)
|
||||
return FALSE
|
||||
return
|
||||
if((I.flags_2 & (HOLOGRAM_2 | NO_MAT_REDEMPTION_2)) || (tc && !is_type_in_typecache(I, tc)))
|
||||
to_chat(user, "<span class='warning'>[parent] won't accept [I]!</span>")
|
||||
return FALSE
|
||||
. = TRUE
|
||||
return
|
||||
. = COMPONENT_ACTIVATED | COMPONENT_NO_AFTERATTACK
|
||||
last_insert_success = FALSE
|
||||
var/datum/callback/pc = precondition
|
||||
if(pc && !pc.Invoke())
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
//Thing meant for allowing datums and objects to access a NTnet network datum.
|
||||
/datum/proc/ntnet_recieve(datum/netdata/data)
|
||||
return
|
||||
|
||||
/datum/proc/ntnet_send(datum/netdata/data, netid)
|
||||
GET_COMPONENT(NIC, /datum/component/ntnet_interface)
|
||||
if(!NIC)
|
||||
return FALSE
|
||||
return NIC.__network_send(data, netid)
|
||||
|
||||
/datum/component/ntnet_interface
|
||||
var/hardware_id //text
|
||||
var/network_name = "" //text
|
||||
var/list/networks_connected_by_id = list() //id = datum/ntnet
|
||||
|
||||
/datum/component/ntnet_interface/Initialize(force_ID, force_name = "NTNet Device", autoconnect_station_network = TRUE) //Don't force ID unless you know what you're doing!
|
||||
if(!force_ID)
|
||||
hardware_id = "[SSnetworks.assignment_hardware_id++]"
|
||||
else
|
||||
hardware_id = force_ID
|
||||
network_name = force_name
|
||||
SSnetworks.register_interface(src)
|
||||
if(autoconnect_station_network)
|
||||
register_connection(SSnetworks.station_network)
|
||||
|
||||
/datum/component/ntnet_interface/Destroy()
|
||||
unregister_all_connections()
|
||||
SSnetworks.unregister_interface(src)
|
||||
return ..()
|
||||
|
||||
/datum/component/ntnet_interface/proc/__network_recieve(datum/netdata/data) //Do not directly proccall!
|
||||
parent.SendSignal(COMSIG_COMPONENT_NTNET_RECIEVE, data)
|
||||
parent.ntnet_recieve(data)
|
||||
|
||||
/datum/component/ntnet_interface/proc/__network_send(datum/netdata/data, netid) //Do not directly proccall!
|
||||
if(netid)
|
||||
if(networks_connected_by_id[netid])
|
||||
var/datum/ntnet/net = networks_connected_by_id[netid]
|
||||
return net.process_data_transmit(src, data)
|
||||
return FALSE
|
||||
for(var/i in networks_connected_by_id)
|
||||
var/datum/ntnet/net = networks_connected_by_id[i]
|
||||
net.process_data_transmit(src, data)
|
||||
return TRUE
|
||||
|
||||
/datum/component/ntnet_interface/proc/register_connection(datum/ntnet/net)
|
||||
if(net.interface_connect(src))
|
||||
networks_connected_by_id[net.network_id] = net
|
||||
return TRUE
|
||||
|
||||
/datum/component/ntnet_interface/proc/unregister_all_connections()
|
||||
for(var/i in networks_connected_by_id)
|
||||
unregister_connection(networks_connected_by_id[i])
|
||||
return TRUE
|
||||
|
||||
/datum/component/ntnet_interface/proc/unregister_connection(datum/ntnet/net)
|
||||
net.interface_disconnect(src)
|
||||
networks_connected_by_id -= net.network_id
|
||||
return TRUE
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
/datum/component/spraycan_paintable/proc/Repaint(obj/item/toy/crayon/spraycan/spraycan, mob/living/user)
|
||||
if(!istype(spraycan) || user.a_intent == INTENT_HARM)
|
||||
return FALSE
|
||||
. = TRUE
|
||||
return
|
||||
. = COMPONENT_NO_AFTERATTACK
|
||||
if(spraycan.is_capped)
|
||||
to_chat(user, "<span class='warning'>Take the cap off first!</span>")
|
||||
return
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
/datum/component/riding
|
||||
var/next_vehicle_move = 0 //used for move delays
|
||||
var/vehicle_move_delay = 2 //tick delay between movements, lower = faster, higher = slower
|
||||
var/keytype
|
||||
|
||||
var/slowed = FALSE
|
||||
var/slowvalue = 1
|
||||
|
||||
var/list/riding_offsets = list() //position_of_user = list(dir = list(px, py)), or RIDING_OFFSET_ALL for a generic one.
|
||||
var/list/directional_vehicle_layers = list() //["[DIRECTION]"] = layer. Don't set it for a direction for default, set a direction to null for no change.
|
||||
var/list/directional_vehicle_offsets = list() //same as above but instead of layer you have a list(px, py)
|
||||
var/list/allowed_turf_typecache
|
||||
var/list/forbid_turf_typecache //allow typecache for only certain turfs, forbid to allow all but those. allow only certain turfs will take precedence.
|
||||
var/allow_one_away_from_valid_turf = TRUE //allow moving one tile away from a valid turf but not more.
|
||||
var/override_allow_spacemove = FALSE
|
||||
var/drive_verb = "drive"
|
||||
var/ride_check_rider_incapacitated = FALSE
|
||||
var/ride_check_rider_restrained = FALSE
|
||||
var/ride_check_ridden_incapacitated = FALSE
|
||||
|
||||
/datum/component/riding/Initialize()
|
||||
if(!ismovableatom(parent))
|
||||
. = COMPONENT_INCOMPATIBLE
|
||||
CRASH("RIDING COMPONENT ASSIGNED TO NON ATOM MOVABLE!")
|
||||
RegisterSignal(COMSIG_MOVABLE_BUCKLE, .proc/vehicle_mob_buckle)
|
||||
RegisterSignal(COMSIG_MOVABLE_UNBUCKLE, .proc/vehicle_mob_unbuckle)
|
||||
RegisterSignal(COMSIG_MOVABLE_MOVED, .proc/vehicle_moved)
|
||||
|
||||
/datum/component/riding/proc/vehicle_mob_unbuckle(mob/living/M, force = FALSE)
|
||||
restore_position(M)
|
||||
unequip_buckle_inhands(M)
|
||||
|
||||
/datum/component/riding/proc/vehicle_mob_buckle(mob/living/M, force = FALSE)
|
||||
handle_vehicle_offsets()
|
||||
|
||||
/datum/component/riding/proc/handle_vehicle_layer()
|
||||
var/atom/movable/AM = parent
|
||||
var/static/list/defaults = list(TEXT_NORTH = OBJ_LAYER, TEXT_SOUTH = ABOVE_MOB_LAYER, TEXT_EAST = ABOVE_MOB_LAYER, TEXT_WEST = ABOVE_MOB_LAYER)
|
||||
. = defaults["[AM.dir]"]
|
||||
if(directional_vehicle_layers["[AM.dir]"])
|
||||
. = directional_vehicle_layers["[AM.dir]"]
|
||||
if(isnull(.)) //you can set it to null to not change it.
|
||||
. = AM.layer
|
||||
AM.layer = .
|
||||
|
||||
/datum/component/riding/proc/set_vehicle_dir_layer(dir, layer)
|
||||
directional_vehicle_layers["[dir]"] = layer
|
||||
|
||||
/datum/component/riding/proc/vehicle_moved()
|
||||
var/atom/movable/AM = parent
|
||||
for(var/i in AM.buckled_mobs)
|
||||
ride_check(i)
|
||||
handle_vehicle_offsets()
|
||||
handle_vehicle_layer()
|
||||
|
||||
/datum/component/riding/proc/ride_check(mob/living/M)
|
||||
var/atom/movable/AM = parent
|
||||
var/mob/AMM = AM
|
||||
if((ride_check_rider_restrained && M.restrained(TRUE)) || (ride_check_rider_incapacitated && M.incapacitated(FALSE, TRUE)) || (ride_check_ridden_incapacitated && istype(AMM) && AMM.incapacitated(FALSE, TRUE)))
|
||||
AM.visible_message("<span class='warning'>[M] falls off of [AM]!</span>")
|
||||
AM.unbuckle_mob(M)
|
||||
return TRUE
|
||||
|
||||
/datum/component/riding/proc/force_dismount(mob/living/M)
|
||||
var/atom/movable/AM = parent
|
||||
AM.unbuckle_mob(M)
|
||||
|
||||
/datum/component/riding/proc/handle_vehicle_offsets()
|
||||
var/atom/movable/AM = parent
|
||||
var/AM_dir = "[AM.dir]"
|
||||
var/passindex = 0
|
||||
if(AM.has_buckled_mobs())
|
||||
for(var/m in AM.buckled_mobs)
|
||||
passindex++
|
||||
var/mob/living/buckled_mob = m
|
||||
var/list/offsets = get_offsets(passindex)
|
||||
var/rider_dir = get_rider_dir(passindex)
|
||||
buckled_mob.setDir(rider_dir)
|
||||
dir_loop:
|
||||
for(var/offsetdir in offsets)
|
||||
if(offsetdir == AM_dir)
|
||||
var/list/diroffsets = offsets[offsetdir]
|
||||
buckled_mob.pixel_x = diroffsets[1]
|
||||
if(diroffsets.len >= 2)
|
||||
buckled_mob.pixel_y = diroffsets[2]
|
||||
if(diroffsets.len == 3)
|
||||
buckled_mob.layer = diroffsets[3]
|
||||
break dir_loop
|
||||
var/list/static/default_vehicle_pixel_offsets = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
|
||||
var/px = default_vehicle_pixel_offsets[AM_dir]
|
||||
var/py = default_vehicle_pixel_offsets[AM_dir]
|
||||
if(directional_vehicle_offsets[AM_dir])
|
||||
if(isnull(directional_vehicle_offsets[AM_dir]))
|
||||
px = AM.pixel_x
|
||||
py = AM.pixel_y
|
||||
else
|
||||
px = directional_vehicle_offsets[AM_dir][1]
|
||||
py = directional_vehicle_offsets[AM_dir][2]
|
||||
AM.pixel_x = px
|
||||
AM.pixel_y = py
|
||||
|
||||
/datum/component/riding/proc/set_vehicle_dir_offsets(dir, x, y)
|
||||
directional_vehicle_offsets["[dir]"] = list(x, y)
|
||||
|
||||
//Override this to set your vehicle's various pixel offsets
|
||||
/datum/component/riding/proc/get_offsets(pass_index) // list(dir = x, y, layer)
|
||||
. = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
|
||||
if(riding_offsets["[pass_index]"])
|
||||
. = riding_offsets["[pass_index]"]
|
||||
else if(riding_offsets["[RIDING_OFFSET_ALL]"])
|
||||
. = riding_offsets["[RIDING_OFFSET_ALL]"]
|
||||
|
||||
/datum/component/riding/proc/set_riding_offsets(index, list/offsets)
|
||||
if(!islist(offsets))
|
||||
return FALSE
|
||||
riding_offsets["[index]"] = offsets
|
||||
|
||||
//Override this to set the passengers/riders dir based on which passenger they are.
|
||||
//ie: rider facing the vehicle's dir, but passenger 2 facing backwards, etc.
|
||||
/datum/component/riding/proc/get_rider_dir(pass_index)
|
||||
var/atom/movable/AM = parent
|
||||
return AM.dir
|
||||
|
||||
//KEYS
|
||||
/datum/component/riding/proc/keycheck(mob/user)
|
||||
return !keytype || user.is_holding_item_of_type(keytype)
|
||||
|
||||
//BUCKLE HOOKS
|
||||
/datum/component/riding/proc/restore_position(mob/living/buckled_mob)
|
||||
if(buckled_mob)
|
||||
buckled_mob.pixel_x = 0
|
||||
buckled_mob.pixel_y = 0
|
||||
if(buckled_mob.client)
|
||||
buckled_mob.client.change_view(world.view)
|
||||
|
||||
//MOVEMENT
|
||||
/datum/component/riding/proc/turf_check(turf/next, turf/current)
|
||||
if(allowed_turf_typecache && !allowed_turf_typecache[next.type])
|
||||
return (allow_one_away_from_valid_turf && allowed_turf_typecache[current.type])
|
||||
else if(forbid_turf_typecache && forbid_turf_typecache[next.type])
|
||||
return (allow_one_away_from_valid_turf && !forbid_turf_typecache[current.type])
|
||||
return TRUE
|
||||
|
||||
/datum/component/riding/proc/handle_ride(mob/user, direction)
|
||||
var/atom/movable/AM = parent
|
||||
if(user.incapacitated())
|
||||
Unbuckle(user)
|
||||
return
|
||||
|
||||
if(world.time < next_vehicle_move)
|
||||
return
|
||||
next_vehicle_move = world.time + vehicle_move_delay
|
||||
|
||||
if(keycheck(user))
|
||||
var/turf/next = get_step(AM, direction)
|
||||
var/turf/current = get_turf(AM)
|
||||
if(!istype(next) || !istype(current))
|
||||
return //not happening.
|
||||
if(!turf_check(next, current))
|
||||
to_chat(user, "Your \the [AM] can not go onto [next]!")
|
||||
return
|
||||
if(!Process_Spacemove(direction) || !isturf(AM.loc))
|
||||
return
|
||||
step(AM, direction)
|
||||
|
||||
handle_vehicle_layer()
|
||||
handle_vehicle_offsets()
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You'll need the keys in one of your hands to [drive_verb] [AM].</span>")
|
||||
|
||||
/datum/component/riding/proc/Unbuckle(atom/movable/M)
|
||||
addtimer(CALLBACK(parent, /atom/movable/.proc/unbuckle_mob, M), 0, TIMER_UNIQUE)
|
||||
|
||||
/datum/component/riding/proc/Process_Spacemove(direction)
|
||||
var/atom/movable/AM = parent
|
||||
return override_allow_spacemove || AM.has_gravity()
|
||||
|
||||
/datum/component/riding/proc/account_limbs(mob/living/M)
|
||||
if(M.get_num_legs() < 2 && !slowed)
|
||||
vehicle_move_delay = vehicle_move_delay + slowvalue
|
||||
slowed = TRUE
|
||||
else if(slowed)
|
||||
vehicle_move_delay = vehicle_move_delay - slowvalue
|
||||
slowed = FALSE
|
||||
|
||||
///////Yes, I said humans. No, this won't end well...//////////
|
||||
/datum/component/riding/human
|
||||
|
||||
/datum/component/riding/human/Initialize()
|
||||
. = ..()
|
||||
RegisterSignal(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_host_unarmed_melee)
|
||||
|
||||
/datum/component/riding/human/proc/on_host_unarmed_melee(atom/target)
|
||||
var/mob/living/carbon/human/AM = parent
|
||||
if(AM.a_intent == INTENT_DISARM && (target in AM.buckled_mobs))
|
||||
force_dismount(target)
|
||||
|
||||
/datum/component/riding/human/handle_vehicle_layer()
|
||||
var/atom/movable/AM = parent
|
||||
if(AM.buckled_mobs && AM.buckled_mobs.len)
|
||||
if(AM.dir == SOUTH)
|
||||
AM.layer = ABOVE_MOB_LAYER
|
||||
else
|
||||
AM.layer = OBJ_LAYER
|
||||
else
|
||||
AM.layer = MOB_LAYER
|
||||
|
||||
/datum/component/riding/human/force_dismount(mob/living/user)
|
||||
var/atom/movable/AM = parent
|
||||
AM.unbuckle_mob(user)
|
||||
user.Knockdown(60)
|
||||
user.visible_message("<span class='warning'>[AM] pushes [user] off of them!</span>")
|
||||
|
||||
/datum/component/riding/cyborg
|
||||
|
||||
/datum/component/riding/cyborg/ride_check(mob/user)
|
||||
var/atom/movable/AM = parent
|
||||
if(user.incapacitated())
|
||||
var/kick = TRUE
|
||||
if(iscyborg(AM))
|
||||
var/mob/living/silicon/robot/R = AM
|
||||
if(R.module && R.module.ride_allow_incapacitated)
|
||||
kick = FALSE
|
||||
if(kick)
|
||||
to_chat(user, "<span class='userdanger'>You fall off of [AM]!</span>")
|
||||
Unbuckle(user)
|
||||
return
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/carbonuser = user
|
||||
if(!carbonuser.get_num_arms())
|
||||
Unbuckle(user)
|
||||
to_chat(user, "<span class='userdanger'>You can't grab onto [AM] with no hands!</span>")
|
||||
return
|
||||
|
||||
/datum/component/riding/cyborg/handle_vehicle_layer()
|
||||
var/atom/movable/AM = parent
|
||||
if(AM.buckled_mobs && AM.buckled_mobs.len)
|
||||
if(AM.dir == SOUTH)
|
||||
AM.layer = ABOVE_MOB_LAYER
|
||||
else
|
||||
AM.layer = OBJ_LAYER
|
||||
else
|
||||
AM.layer = MOB_LAYER
|
||||
|
||||
/datum/component/riding/cyborg/get_offsets(pass_index) // list(dir = x, y, layer)
|
||||
return list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(-6, 3), TEXT_WEST = list( 6, 3))
|
||||
|
||||
/datum/component/riding/cyborg/handle_vehicle_offsets()
|
||||
var/atom/movable/AM = parent
|
||||
if(AM.has_buckled_mobs())
|
||||
for(var/mob/living/M in AM.buckled_mobs)
|
||||
M.setDir(AM.dir)
|
||||
if(iscyborg(AM))
|
||||
var/mob/living/silicon/robot/R = AM
|
||||
if(istype(R.module))
|
||||
M.pixel_x = R.module.ride_offset_x[dir2text(AM.dir)]
|
||||
M.pixel_y = R.module.ride_offset_y[dir2text(AM.dir)]
|
||||
else
|
||||
..()
|
||||
|
||||
/datum/component/riding/cyborg/force_dismount(mob/living/M)
|
||||
var/atom/movable/AM = parent
|
||||
AM.unbuckle_mob(M)
|
||||
var/turf/target = get_edge_target_turf(AM, AM.dir)
|
||||
var/turf/targetm = get_step(get_turf(AM), AM.dir)
|
||||
M.Move(targetm)
|
||||
M.visible_message("<span class='warning'>[M] is thrown clear of [AM]!</span>")
|
||||
M.throw_at(target, 14, 5, AM)
|
||||
M.Knockdown(60)
|
||||
|
||||
/datum/component/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1)
|
||||
var/atom/movable/AM = parent
|
||||
var/amount_equipped = 0
|
||||
for(var/amount_needed = amount_required, amount_needed > 0, amount_needed--)
|
||||
var/obj/item/riding_offhand/inhand = new /obj/item/riding_offhand(user)
|
||||
inhand.rider = user
|
||||
inhand.parent = AM
|
||||
if(user.put_in_hands(inhand, TRUE))
|
||||
amount_equipped++
|
||||
else
|
||||
break
|
||||
if(amount_equipped >= amount_required)
|
||||
return TRUE
|
||||
else
|
||||
unequip_buckle_inhands(user)
|
||||
return FALSE
|
||||
|
||||
/datum/component/riding/proc/unequip_buckle_inhands(mob/living/carbon/user)
|
||||
var/atom/movable/AM = parent
|
||||
for(var/obj/item/riding_offhand/O in user.contents)
|
||||
if(O.parent != AM)
|
||||
CRASH("RIDING OFFHAND ON WRONG MOB")
|
||||
continue
|
||||
if(O.selfdeleting)
|
||||
continue
|
||||
else
|
||||
qdel(O)
|
||||
return TRUE
|
||||
|
||||
/obj/item/riding_offhand
|
||||
name = "offhand"
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
icon_state = "offhand"
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
flags_1 = ABSTRACT_1 | DROPDEL_1 | NOBLUDGEON_1
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
var/mob/living/carbon/rider
|
||||
var/mob/living/parent
|
||||
var/selfdeleting = FALSE
|
||||
|
||||
/obj/item/riding_offhand/dropped()
|
||||
selfdeleting = TRUE
|
||||
. = ..()
|
||||
|
||||
/obj/item/riding_offhand/equipped()
|
||||
if(loc != rider)
|
||||
selfdeleting = TRUE
|
||||
qdel(src)
|
||||
. = ..()
|
||||
|
||||
/obj/item/riding_offhand/Destroy()
|
||||
var/atom/movable/AM = parent
|
||||
if(selfdeleting)
|
||||
if(rider in AM.buckled_mobs)
|
||||
AM.unbuckle_mob(rider)
|
||||
. = ..()
|
||||
@@ -12,7 +12,7 @@
|
||||
var/mob/victim = AM
|
||||
if(istype(victim) && !victim.is_flying() && victim.slip(intensity, parent, lube_flags))
|
||||
slip_victim = victim
|
||||
return TRUE
|
||||
return COMPONENT_ACTIVATED
|
||||
|
||||
/datum/component/slippery/AfterComponentActivated()
|
||||
slip_victim = null
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
if(ishuman(C))
|
||||
var/mob/living/carbon/human/H = C
|
||||
if(istype(H.dna.species, /datum/species/skeleton))
|
||||
return ..() //undeads are unaffected by the spook-pocalypse.
|
||||
return //undeads are unaffected by the spook-pocalypse.
|
||||
if(istype(H.dna.species, /datum/species/zombie))
|
||||
H.adjustStaminaLoss(25)
|
||||
H.Knockdown(15) //zombies can't resist the doot
|
||||
|
||||
@@ -3,13 +3,30 @@
|
||||
var/amount
|
||||
var/overlay
|
||||
|
||||
var/static/list/blacklist = typecacheof(/turf/closed/wall/mineral/diamond)
|
||||
var/static/list/resistlist = typecacheof(/turf/closed/wall/r_wall)
|
||||
var/static/list/blacklist = typecacheof(
|
||||
/turf/open/lava,
|
||||
/turf/open/space,
|
||||
/turf/open/water,
|
||||
/turf/open/chasm,
|
||||
)
|
||||
|
||||
var/static/list/immunelist = typecacheof(
|
||||
/turf/closed/wall/mineral/diamond,
|
||||
/turf/closed/indestructible,
|
||||
/turf/open/indestructible,
|
||||
)
|
||||
|
||||
var/static/list/resistlist = typecacheof(
|
||||
/turf/closed/wall/r_wall,
|
||||
)
|
||||
|
||||
/datum/component/thermite/Initialize(_amount)
|
||||
if(!istype(parent, /turf))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
. = COMPONENT_INCOMPATIBLE
|
||||
CRASH("A thermite component has been applied to an incorrect object. parent: [parent]")
|
||||
if(blacklist[parent.type])
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
if(immunelist[parent.type])
|
||||
_amount*=0 //Yeah the overlay can still go on it and be cleaned but you arent burning down a diamond wall
|
||||
if(resistlist[parent.type])
|
||||
_amount*=0.25
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
GLOBAL_LIST_EMPTY(uplinks)
|
||||
|
||||
/**
|
||||
* Uplinks
|
||||
*
|
||||
* All /obj/item(s) have a hidden_uplink var. By default it's null. Give the item one with 'new(src') (it must be in it's contents). Then add 'uses.'
|
||||
* Use whatever conditionals you want to check that the user has an uplink, and then call interact() on their uplink.
|
||||
* You might also want the uplink menu to open if active. Check if the uplink is 'active' and then interact() with it.
|
||||
**/
|
||||
/datum/component/uplink
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE
|
||||
var/name = "syndicate uplink"
|
||||
var/active = FALSE
|
||||
var/lockable = TRUE
|
||||
var/locked = TRUE
|
||||
var/telecrystals
|
||||
var/selected_cat
|
||||
var/owner = null
|
||||
var/datum/game_mode/gamemode
|
||||
var/spent_telecrystals = 0
|
||||
var/datum/uplink_purchase_log/purchase_log
|
||||
var/list/uplink_items
|
||||
var/hidden_crystals = 0
|
||||
|
||||
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20)
|
||||
if(!isitem(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
GLOB.uplinks += src
|
||||
uplink_items = get_uplink_items(gamemode)
|
||||
RegisterSignal(COMSIG_PARENT_ATTACKBY, .proc/OnAttackBy)
|
||||
RegisterSignal(COMSIG_ITEM_ATTACK_SELF, .proc/interact)
|
||||
owner = _owner
|
||||
if(owner)
|
||||
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
|
||||
if(GLOB.uplink_purchase_logs_by_key[owner])
|
||||
purchase_log = GLOB.uplink_purchase_logs_by_key[owner]
|
||||
else
|
||||
purchase_log = new(owner, src)
|
||||
lockable = _lockable
|
||||
active = _enabled
|
||||
gamemode = _gamemode
|
||||
telecrystals = starting_tc
|
||||
if(!lockable)
|
||||
active = TRUE
|
||||
locked = FALSE
|
||||
|
||||
/datum/component/uplink/InheritComponent(datum/component/uplink/U)
|
||||
lockable |= U.lockable
|
||||
active |= U.active
|
||||
if(!gamemode)
|
||||
gamemode = U.gamemode
|
||||
telecrystals += U.telecrystals
|
||||
if(purchase_log && U.purchase_log)
|
||||
purchase_log.MergeWithAndDel(U.purchase_log)
|
||||
|
||||
/datum/component/uplink/Destroy()
|
||||
GLOB.uplinks -= src
|
||||
gamemode = null
|
||||
return ..()
|
||||
|
||||
/datum/component/uplink/proc/LoadTC(mob/user, obj/item/stack/telecrystal/TC, silent = FALSE)
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='notice'>You slot [TC] into [parent] and charge its internal uplink.</span>")
|
||||
var/amt = TC.amount
|
||||
telecrystals += amt
|
||||
TC.use(amt)
|
||||
|
||||
/datum/component/uplink/proc/set_gamemode(_gamemode)
|
||||
gamemode = _gamemode
|
||||
uplink_items = get_uplink_items(gamemode)
|
||||
|
||||
/datum/component/uplink/proc/OnAttackBy(obj/item/I, mob/user)
|
||||
if(!active)
|
||||
return //no hitting everyone/everything just to try to slot tcs in!
|
||||
if(istype(I, /obj/item/stack/telecrystal))
|
||||
LoadTC(user, I)
|
||||
for(var/item in subtypesof(/datum/uplink_item))
|
||||
var/datum/uplink_item/UI = item
|
||||
var/path = null
|
||||
if(initial(UI.refund_path))
|
||||
path = initial(UI.refund_path)
|
||||
else
|
||||
path = initial(UI.item)
|
||||
var/cost = 0
|
||||
if(initial(UI.refund_amount))
|
||||
cost = initial(UI.refund_amount)
|
||||
else
|
||||
cost = initial(UI.cost)
|
||||
var/refundable = initial(UI.refundable)
|
||||
if(I.type == path && refundable && I.check_uplink_validity())
|
||||
telecrystals += cost
|
||||
spent_telecrystals -= cost
|
||||
to_chat(user, "<span class='notice'>[I] refunded.</span>")
|
||||
qdel(I)
|
||||
return
|
||||
|
||||
/datum/component/uplink/proc/interact(mob/user)
|
||||
if(locked)
|
||||
return
|
||||
active = TRUE
|
||||
if(user)
|
||||
ui_interact(user)
|
||||
|
||||
/datum/component/uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
|
||||
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
|
||||
active = TRUE
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "uplink", name, 450, 750, master_ui, state)
|
||||
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
|
||||
ui.set_style("syndicate")
|
||||
ui.open()
|
||||
|
||||
/datum/component/uplink/ui_data(mob/user)
|
||||
if(!user.mind)
|
||||
return
|
||||
var/list/data = list()
|
||||
data["telecrystals"] = telecrystals
|
||||
data["lockable"] = lockable
|
||||
|
||||
data["categories"] = list()
|
||||
for(var/category in uplink_items)
|
||||
var/list/cat = list(
|
||||
"name" = category,
|
||||
"items" = (category == selected_cat ? list() : null))
|
||||
if(category == selected_cat)
|
||||
for(var/item in uplink_items[category])
|
||||
var/datum/uplink_item/I = uplink_items[category][item]
|
||||
if(I.limited_stock == 0)
|
||||
continue
|
||||
if(I.restricted_roles.len)
|
||||
var/is_inaccessible = 1
|
||||
for(var/R in I.restricted_roles)
|
||||
if(R == user.mind.assigned_role)
|
||||
is_inaccessible = 0
|
||||
if(is_inaccessible)
|
||||
continue
|
||||
cat["items"] += list(list(
|
||||
"name" = I.name,
|
||||
"cost" = I.cost,
|
||||
"desc" = I.desc,
|
||||
))
|
||||
data["categories"] += list(cat)
|
||||
return data
|
||||
|
||||
/datum/component/uplink/ui_act(action, params)
|
||||
if(!active)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("buy")
|
||||
var/item = params["item"]
|
||||
|
||||
var/list/buyable_items = list()
|
||||
for(var/category in uplink_items)
|
||||
buyable_items += uplink_items[category]
|
||||
|
||||
if(item in buyable_items)
|
||||
var/datum/uplink_item/I = buyable_items[item]
|
||||
MakePurchase(usr, I)
|
||||
. = TRUE
|
||||
if("lock")
|
||||
active = FALSE
|
||||
locked = TRUE
|
||||
telecrystals += hidden_crystals
|
||||
hidden_crystals = 0
|
||||
SStgui.close_uis(src)
|
||||
if("select")
|
||||
selected_cat = params["category"]
|
||||
return TRUE
|
||||
|
||||
/datum/component/uplink/proc/MakePurchase(mob/user, datum/uplink_item/U)
|
||||
if(!istype(U))
|
||||
return
|
||||
if (!user || user.incapacitated())
|
||||
return
|
||||
|
||||
if(telecrystals < U.cost || U.limited_stock == 0)
|
||||
return
|
||||
telecrystals -= U.cost
|
||||
|
||||
var/atom/A = U.spawn_item(get_turf(user), src, user)
|
||||
if(U.purchase_log_vis && purchase_log)
|
||||
var/obj/item/storage/box/B = A
|
||||
var/list/atom/logging = list()
|
||||
if(istype(B) && B.contents.len > 0)
|
||||
logging |= list(B)
|
||||
else
|
||||
logging |= A
|
||||
for(var/atom/_logging in logging)
|
||||
purchase_log.LogPurchase(_logging, U.cost)
|
||||
|
||||
if(U.limited_stock > 0)
|
||||
U.limited_stock -= 1
|
||||
|
||||
SSblackbox.record_feedback("nested tally", "traitor_uplink_items_bought", 1, list("[initial(U.name)]", "[U.cost]"))
|
||||
if(ishuman(user) && istype(A, /obj/item))
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(H.put_in_hands(A))
|
||||
to_chat(H, "[A] materializes into your hands!")
|
||||
else
|
||||
to_chat(H, "\The [A] materializes onto the floor.")
|
||||
return TRUE
|
||||
|
||||
Reference in New Issue
Block a user