diff --git a/code/__defines/directions.dm b/code/__defines/directions.dm
index b4524560447..4f6bff8a3a0 100644
--- a/code/__defines/directions.dm
+++ b/code/__defines/directions.dm
@@ -6,7 +6,7 @@
#define N_NORTHEAST 32
#define N_NORTHWEST 512
#define N_SOUTHEAST 64
-#define N_SOUTHWEST 1024
+#define N_SOUTHWEST 1024
#define CORNER_NONE 0
#define CORNER_COUNTERCLOCKWISE 1
@@ -43,6 +43,22 @@
return ret
+/proc/corner_states_to_dirs(list/corners)
+ if(!istype(corners)) return
+
+ var/list/ret = list(NORTHWEST, SOUTHEAST, NORTHEAST, SOUTHWEST)
+ . = list()
+
+ for(var/i = 1 to ret.len)
+ var/dir = ret[i]
+ var/corner = text2num(corners[i])
+ if(corner & CORNER_DIAGONAL)
+ . |= dir
+ if(corner & CORNER_COUNTERCLOCKWISE)
+ . |= turn(dir, 45)
+ if(corner & CORNER_CLOCKWISE)
+ . |= turn(dir, -45)
+
// Similar to dirs_to_corner_states(), but returns an *ordered* list, requiring (in order), dir=NORTH, SOUTH, EAST, WEST
// Note that this means this proc can be used as:
diff --git a/code/__defines/subsystem-priority.dm b/code/__defines/subsystem-priority.dm
index 85ce85d0d06..a927f04a581 100644
--- a/code/__defines/subsystem-priority.dm
+++ b/code/__defines/subsystem-priority.dm
@@ -46,7 +46,8 @@
#define SS_PRIORITY_PLANTS 90 // Plant processing, slow ticks.
#define SS_PRIORITY_VINES 50 // Spreading vine effects.
#define SS_PRIORITY_PSYCHICS 45 // Psychic complexus processing.
-#define SS_PRIORITY_AI 45 // Artificial Intelligence on mobs processing.
+#define SS_PRIORITY_MOB_AI 45 // Mob AI logic; finding targets, attacking, etc.
+#define SS_PRIORITY_AUTO_MOVE 42 // Automated atom movement, fires much more frequently than MOB_AI.
#define SS_PRIORITY_NANO 40 // Updates to nanoui uis.
#define SS_PRIORITY_TURF 30 // Radioactive walls/blob.
#define SS_PRIORITY_EVAC 30 // Processes the evac controller.
diff --git a/code/_helpers/emissive.dm b/code/_helpers/emissive.dm
index 18ef96c37f8..4ea0ca7e7cf 100644
--- a/code/_helpers/emissive.dm
+++ b/code/_helpers/emissive.dm
@@ -1,14 +1,5 @@
/proc/emissive_overlay(var/icon, var/icon_state, var/loc, var/dir, var/color)
- var/image/emissive/I = new(icon, icon_state)
- if(!isnull(loc))
- I.loc = loc
- if(!isnull(dir))
- I.dir = dir
- if(!isnull(color))
- I.color = color
+ var/image/I = image(icon, loc, icon_state, EMISSIVE_LAYER, dir)
+ I.plane = EMISSIVE_PLANE
+ I.color = color
return I
-
-/image/emissive/New()
- ..()
- layer = EMISSIVE_LAYER
- plane = EMISSIVE_PLANE
diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm
index 069508a1767..eea2b9b3833 100644
--- a/code/_helpers/time.dm
+++ b/code/_helpers/time.dm
@@ -98,7 +98,7 @@ var/global/round_start_time = 0
return last_round_duration
var/mills = round_duration_in_ticks // 1/10 of a second, not real milliseconds but whatever
- //var/secs = ((mills % 36000) % 600) / 10 //Not really needed, but I'll leave it here for refrence.. or something
+ //var/secs = ((mills % 36000) % 600) / 10 //Not really needed, but I'll leave it here for reference or something
var/mins = round((mills % 36000) / 600)
var/hours = round(mills / 36000)
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index d8cd03cffa3..83b61932630 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -114,15 +114,13 @@
//Atoms on your person
// A is your location but is not a turf; or is on you (backpack); or is on something on you (box in backpack); sdepth is needed here because contents depth does not equate inventory storage depth.
var/sdepth = A.storage_depth(src)
- var/check_dexterity_val = A.storage ? DEXTERITY_NONE : (istype(holding) ? holding.needs_attack_dexterity : DEXTERITY_WIELD_ITEM)
- var/can_wield_item = holding && (!check_dexterity_val || check_dexterity(check_dexterity_val))
if((!isturf(A) && A == loc) || (sdepth != -1 && sdepth <= 1))
- if(can_wield_item)
+ if(holding)
var/resolved = holding.resolve_attackby(A, src, params)
if(!resolved && A && holding)
holding.afterattack(A, src, 1, params) // 1 indicates adjacency
setClickCooldown(DEFAULT_QUICK_COOLDOWN)
- else if(!holding)
+ else
if(ismob(A)) // No instant mob attacking
setClickCooldown(DEFAULT_QUICK_COOLDOWN)
UnarmedAttack(A, TRUE)
@@ -138,13 +136,13 @@
sdepth = A.storage_depth_turf()
if(isturf(A) || isturf(A.loc) || (sdepth != -1 && sdepth <= 1))
if(A.Adjacent(src)) // see adjacent.dm
- if(can_wield_item)
+ if(holding)
// Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example)
var/resolved = holding.resolve_attackby(A,src, params)
if(!resolved && A && holding)
holding.afterattack(A, src, 1, params) // 1: clicking something Adjacent
setClickCooldown(DEFAULT_QUICK_COOLDOWN)
- else if(!holding)
+ else
if(ismob(A)) // No instant mob attacking
setClickCooldown(DEFAULT_QUICK_COOLDOWN)
UnarmedAttack(A, TRUE)
diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm
index 066fe948bfd..535604b5e5d 100644
--- a/code/_onclick/cyborg.dm
+++ b/code/_onclick/cyborg.dm
@@ -71,11 +71,6 @@
holding.attack_self(src)
return
- var/check_dexterity_val = A.storage ? DEXTERITY_NONE : (istype(holding) ? holding.needs_attack_dexterity : DEXTERITY_WIELD_ITEM)
- var/can_wield_item = (!check_dexterity_val || check_dexterity(check_dexterity_val))
- if(!can_wield_item)
- return
-
if(A == loc || (A in loc) || (A in contents))
// No adjacency checks
var/resolved = holding.resolve_attackby(A, src, params)
diff --git a/code/_onclick/hud/animal.dm b/code/_onclick/hud/animal.dm
index e0eed1acee3..5c1d9173a3a 100644
--- a/code/_onclick/hud/animal.dm
+++ b/code/_onclick/hud/animal.dm
@@ -3,7 +3,14 @@
hud_used = /datum/hud/animal
/datum/hud/animal/FinalizeInstantiation()
- action_intent = new(null, mymob, get_ui_style_data(), get_ui_color(), get_ui_alpha(), UI_ICON_INTENT)
+
+ var/ui_style = get_ui_style_data()
+ var/ui_color = get_ui_color()
+ var/ui_alpha = get_ui_alpha()
+
+ move_intent = new(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_MOVEMENT)
+ move_intent.icon_state = mymob.move_intent.hud_icon_state
+ adding += move_intent
+ action_intent = new(null, mymob, ui_style, ui_color, ui_alpha, UI_ICON_INTENT)
adding += action_intent
..()
-
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index c99039b23e7..b326cbc2b0f 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -44,22 +44,24 @@ avoid code duplication. This includes items that may sometimes act as a standard
if(!.)
return bash(W,user)
-/atom/movable/proc/bash(obj/item/W, mob/user)
+/atom/movable/proc/bash(obj/item/weapon, mob/user)
if(isliving(user) && user.a_intent == I_HELP)
return FALSE
- if(W.item_flags & ITEM_FLAG_NO_BLUDGEON)
+ if(!weapon.user_can_wield(user))
return FALSE
- visible_message("[src] has been hit by [user] with [W].")
+ if(weapon.item_flags & ITEM_FLAG_NO_BLUDGEON)
+ return FALSE
+ visible_message(SPAN_DANGER("[src] has been hit by [user] with [weapon]."))
return TRUE
-/mob/living/attackby(obj/item/I, mob/user)
+/mob/living/attackby(obj/item/used_item, mob/user)
if(!ismob(user))
return TRUE
- if(can_operate(src,user) != OPERATE_DENY && I.do_surgery(src,user)) //Surgery
+ if(can_operate(src, user) != OPERATE_DENY && used_item.do_surgery(src,user)) //Surgery
return TRUE
- if(try_butcher_in_place(user, I))
+ if(try_butcher_in_place(user, used_item))
return TRUE
- return I.use_on_mob(src, user)
+ return used_item.use_on_mob(src, user)
/mob/living/human/attackby(obj/item/I, mob/user)
@@ -96,6 +98,11 @@ avoid code duplication. This includes items that may sometimes act as a standard
//I would prefer to rename this attack_as_weapon(), but that would involve touching hundreds of files.
/obj/item/proc/use_on_mob(mob/living/target, mob/living/user, animate = TRUE)
+ // TODO: revisit if this should be a silent failure/parent call instead, for mob-level storage interactions?
+ // like a horse with a saddlebag or something
+ if(!user_can_wield(user))
+ return TRUE // skip other interactions
+
if(squash_item())
return TRUE
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index 7965251ab03..a43100cd6dd 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -82,7 +82,7 @@
var/decl/pronouns/G = get_pronouns()
face_atom(A)
if(attack_delay)
- walk_to(src, 0) // Cancel any baked-in movement.
+ stop_automove() // Cancel any baked-in movement.
do_windup_animation(A, attack_delay, no_reset = TRUE)
if(!do_after(src, attack_delay, A) || !Adjacent(A))
visible_message(SPAN_NOTICE("\The [src] misses [G.his] attack on \the [A]!"))
diff --git a/code/controllers/subsystems/mob_ai/auto_movement.dm b/code/controllers/subsystems/mob_ai/auto_movement.dm
new file mode 100644
index 00000000000..7e2990c1cb8
--- /dev/null
+++ b/code/controllers/subsystems/mob_ai/auto_movement.dm
@@ -0,0 +1,55 @@
+SUBSYSTEM_DEF(automove)
+ name = "Automated Movement"
+ wait = 1
+ flags = SS_NO_INIT
+ priority = SS_PRIORITY_AUTO_MOVE
+
+ var/list/moving_atoms = list()
+ var/list/moving_metadata = list()
+ var/list/processing_atoms
+
+/datum/controller/subsystem/automove/proc/unregister_mover(atom/movable/mover)
+ if(!istype(mover))
+ CRASH("Invalid parameters to unregister_mover: [mover || "NULL"]")
+ if(length(moving_atoms))
+ moving_atoms -= mover
+ if(length(moving_metadata))
+ moving_metadata -= mover
+ if(length(processing_atoms))
+ processing_atoms -= mover
+
+/datum/controller/subsystem/automove/proc/register_mover(atom/movable/mover, controller_type, datum/automove_metadata/metadata)
+ if(!istype(mover) || (!ispath(controller_type, /decl/automove_controller) && !istype(controller_type, /decl/automove_controller)))
+ CRASH("Invalid parameters to register_mover: [controller_type || "NULL"], [mover || "NULL"]")
+
+ var/decl/automove_controller/controller = ispath(controller_type) ? GET_DECL(controller_type) : controller_type
+ moving_atoms[mover] = controller
+ if(istype(metadata))
+ moving_metadata[mover] = metadata
+ else
+ moving_metadata -= mover
+
+ if(suspended)
+ wake()
+
+/datum/controller/subsystem/automove/fire(resumed = FALSE)
+
+ if(!resumed)
+ processing_atoms = moving_atoms.Copy()
+
+ if(!length(processing_atoms))
+ suspend()
+ return
+
+ var/i = 0
+ var/atom/movable/mover
+ var/decl/automove_controller/controller
+ while(i < processing_atoms.len)
+ i++
+ mover = processing_atoms[i]
+ controller = processing_atoms[mover]
+ if(controller.handle_mover(mover, moving_metadata[mover]) == PROCESS_KILL && !QDELETED(mover))
+ mover.stop_automove()
+ if(MC_TICK_CHECK)
+ processing_atoms.Cut(1, i+1)
+ return
diff --git a/code/controllers/subsystems/mob_ai/mob_ai.dm b/code/controllers/subsystems/mob_ai/mob_ai.dm
new file mode 100644
index 00000000000..9990c62185e
--- /dev/null
+++ b/code/controllers/subsystems/mob_ai/mob_ai.dm
@@ -0,0 +1,3 @@
+PROCESSING_SUBSYSTEM_DEF(mob_ai)
+ name = "Mob AI"
+ priority = SS_PRIORITY_MOB_AI
diff --git a/code/controllers/subsystems/processing/ai.dm b/code/controllers/subsystems/processing/ai.dm
deleted file mode 100644
index ff1ad3de238..00000000000
--- a/code/controllers/subsystems/processing/ai.dm
+++ /dev/null
@@ -1,3 +0,0 @@
-PROCESSING_SUBSYSTEM_DEF(ai)
- name = "AIs"
- priority = SS_PRIORITY_AI
diff --git a/code/datums/ai/ai.dm b/code/datums/ai/ai.dm
index f466d5b6daf..8373aa01c26 100644
--- a/code/datums/ai/ai.dm
+++ b/code/datums/ai/ai.dm
@@ -1,25 +1,25 @@
-/datum/ai
+/datum/mob_controller
var/name
var/mob/living/body // The parent mob we control.
var/expected_type = /mob/living // Type of mob this AI applies to.
var/wait_for = 0 // The next time we can process.
var/run_interval = 1 // How long to wait between processes.
-/datum/ai/New(var/mob/living/target_body)
+/datum/mob_controller/New(var/mob/living/target_body)
body = target_body
if(expected_type && !istype(body, expected_type))
PRINT_STACK_TRACE("AI datum [type] received a body ([body ? body.type : "NULL"]) of unexpected type ([expected_type]).")
- START_PROCESSING(SSai, src)
+ START_PROCESSING(SSmob_ai, src)
-/datum/ai/Destroy()
- STOP_PROCESSING(SSai, src)
+/datum/mob_controller/Destroy()
+ STOP_PROCESSING(SSmob_ai, src)
if(body)
if(body.ai == src)
body.ai = null
body = null
. = ..()
-/datum/ai/proc/can_process()
+/datum/mob_controller/proc/can_process()
if(!body || !body.loc || ((body.client || body.mind) && !(body.status_flags & ENABLE_AI)))
return FALSE
if(wait_for > world.time)
@@ -28,7 +28,7 @@
return FALSE
return TRUE
-/datum/ai/Process()
+/datum/mob_controller/Process()
if(!can_process())
return
@@ -37,5 +37,11 @@
do_process(time_elapsed)
// This is the place to actually do work in the AI.
-/datum/ai/proc/do_process(var/time_elapsed)
+/datum/mob_controller/proc/do_process(var/time_elapsed)
return
+
+/datum/mob_controller/proc/get_automove_target(datum/automove_metadata/metadata)
+ return null
+
+/datum/mob_controller/proc/can_do_automated_move(variant_move_delay)
+ return body && !body.client
diff --git a/code/datums/ai/human.dm b/code/datums/ai/human.dm
index 60be355be6a..7c2eda8f136 100644
--- a/code/datums/ai/human.dm
+++ b/code/datums/ai/human.dm
@@ -1,8 +1,8 @@
-/datum/ai/human
+/datum/mob_controller/human
name = "human"
expected_type = /mob/living/human
-/datum/ai/human/do_process(var/time_elapsed)
+/datum/mob_controller/human/do_process(var/time_elapsed)
var/mob/living/human/H = body
if(H.stat != CONSCIOUS)
return
diff --git a/code/datums/ai/monkey.dm b/code/datums/ai/monkey.dm
index 1226e253809..6f902d87597 100644
--- a/code/datums/ai/monkey.dm
+++ b/code/datums/ai/monkey.dm
@@ -1,4 +1,4 @@
-/datum/ai/monkey
+/datum/mob_controller/monkey
name = "monkey"
expected_type = /mob/living/human
var/list/no_touchie = list(
@@ -6,7 +6,7 @@
/obj/structure/mirror
)
-/datum/ai/monkey/do_process(var/time_elapsed)
+/datum/mob_controller/monkey/do_process(var/time_elapsed)
if(body.incapacitated())
return
diff --git a/code/datums/config/config_types/config_server.dm b/code/datums/config/config_types/config_server.dm
index d94a3a56bdb..b2f5b2a0fcd 100644
--- a/code/datums/config/config_types/config_server.dm
+++ b/code/datums/config/config_types/config_server.dm
@@ -330,7 +330,7 @@
/decl/config/toggle/delist_when_no_admins
uid = "delist_when_no_admins"
- desc = "Determines if the server should hide itself from the hub when no admins are online.."
+ desc = "Determines if the server should hide itself from the hub when no admins are online."
/decl/config/toggle/wait_for_sigusr1_reboot
uid = "wait_for_sigusr1_reboot"
diff --git a/code/datums/extensions/lockable.dm b/code/datums/extensions/lockable.dm
index 797a47c8b14..d3c9aee3cb2 100644
--- a/code/datums/extensions/lockable.dm
+++ b/code/datums/extensions/lockable.dm
@@ -92,6 +92,8 @@
Process keypresses coming from the nanoUI.
*/
/datum/extension/lockable/proc/pressed_key(key_char, mob/user)
+ if(!user.check_dexterity(DEXTERITY_KEYBOARDS))
+ return
// Always clear error when pressing a button.
clear_error()
@@ -318,22 +320,25 @@
/**
Item attack handler for interactions with the host.
*/
-/datum/extension/lockable/proc/attackby(obj/item/W, mob/user)
+/datum/extension/lockable/proc/attackby(obj/item/used_item, mob/user)
if(!locked)
- return
+ return FALSE
+
+ if(!used_item.user_can_wield(user))
+ return TRUE
//TODO: This probably should be handled in a better way.
- if(!is_digital_lock && istype(W, /obj/item/energy_blade))
- var/obj/item/energy_blade/blade = W
+ if(!is_digital_lock && istype(used_item, /obj/item/energy_blade))
+ var/obj/item/energy_blade/blade = used_item
if(blade.is_special_cutting_tool() && emag_act(INFINITY, user, "You slice through the lock of \the [holder]."))
var/obj/item/A = holder
spark_at(A.loc, amount=5)
playsound(A.loc, 'sound/weapons/blade1.ogg', 50, 1)
return TRUE
- if(IS_SCREWDRIVER(W))
+ if(IS_SCREWDRIVER(used_item))
if(!opening_panel)
- var/obj/item/screwdriver/S = W
+ var/obj/item/screwdriver/S = used_item
opening_panel = TRUE //Make sure we only have one user/attempt to opens the panel at a time.
if(
S.do_tool_interaction(
@@ -350,8 +355,8 @@
toggle_panel(user)
return TRUE
- if(IS_MULTITOOL(W))
- try_hack(W, user)
+ if(IS_MULTITOOL(used_item))
+ try_hack(used_item, user)
return TRUE
/**
diff --git a/code/datums/inventory_slots/inventory_gripper_subtypes.dm b/code/datums/inventory_slots/inventory_gripper_subtypes.dm
index 74d766ab4eb..3e72420e047 100644
--- a/code/datums/inventory_slots/inventory_gripper_subtypes.dm
+++ b/code/datums/inventory_slots/inventory_gripper_subtypes.dm
@@ -6,7 +6,7 @@
overlay_slot = BP_MOUTH
ui_label = "M"
hand_sort_priority = 3
- dexterity = DEXTERITY_SIMPLE_MACHINES | DEXTERITY_HOLD_ITEM | DEXTERITY_EQUIP_ITEM | DEXTERITY_KEYBOARDS | DEXTERITY_TOUCHSCREENS
+ dexterity = DEXTERITY_SIMPLE_MACHINES | DEXTERITY_GRAPPLE | DEXTERITY_HOLD_ITEM | DEXTERITY_EQUIP_ITEM | DEXTERITY_KEYBOARDS | DEXTERITY_TOUCHSCREENS
/datum/inventory_slot/gripper/mouth/simple
requires_organ_tag = null
diff --git a/code/datums/move_intent/move_intent.dm b/code/datums/move_intent/move_intent.dm
index 87e521f5e33..b520d57b73d 100644
--- a/code/datums/move_intent/move_intent.dm
+++ b/code/datums/move_intent/move_intent.dm
@@ -1,11 +1,13 @@
// Quick and deliberate movements are not necessarily mutually exclusive
+#define MOVE_INTENT_NONE 0
#define MOVE_INTENT_DELIBERATE BITFLAG(0)
#define MOVE_INTENT_EXERTIVE BITFLAG(1)
#define MOVE_INTENT_QUICK BITFLAG(2)
+#define MOVE_INTENT_NEUTRAL BITFLAG(3)
/decl/move_intent
var/name
- var/flags = 0
+ var/flags = MOVE_INTENT_NONE
var/move_delay = 1
var/hud_icon_state
@@ -26,6 +28,7 @@
/decl/move_intent/walk
name = "Walk"
hud_icon_state = "walking"
+ flags = MOVE_INTENT_NEUTRAL
/decl/move_intent/walk/Initialize()
. = ..()
diff --git a/code/datums/move_intent/move_intent_animal.dm b/code/datums/move_intent/move_intent_animal.dm
new file mode 100644
index 00000000000..e7fc6b376b8
--- /dev/null
+++ b/code/datums/move_intent/move_intent_animal.dm
@@ -0,0 +1,23 @@
+/decl/move_intent/walk/animal
+ move_delay = 3
+
+/decl/move_intent/run/animal
+ move_delay = 2
+
+/decl/move_intent/walk/animal_slow
+ move_delay = 4
+
+/decl/move_intent/run/animal_slow
+ move_delay = 3
+
+/decl/move_intent/walk/animal_very_slow
+ move_delay = 8
+
+/decl/move_intent/run/animal_very_slow
+ move_delay = 6
+
+/decl/move_intent/walk/animal_fast
+ move_delay = 2
+
+/decl/move_intent/run/animal_fast
+ move_delay = 1
diff --git a/code/datums/movement/automove.dm b/code/datums/movement/automove.dm
new file mode 100644
index 00000000000..a07d65d0324
--- /dev/null
+++ b/code/datums/movement/automove.dm
@@ -0,0 +1,40 @@
+// These procs are a way to implement something like walk_to()/walk_away()
+// while also preserving the full move chain for mobs. /obj and such can
+// get away with walk(), but mobs need to set move delays, update glide size, etc.
+
+/atom/movable/proc/get_default_automove_controller_type()
+ return /decl/automove_controller
+
+/// Cancels automoving and unregisters the atom from the subsystem, including the current processing run.
+/atom/movable/proc/stop_automove()
+ SHOULD_CALL_PARENT(TRUE)
+ walk_to(src, 0) // Legacy call to stop BYOND's inbuilt movement.
+ SSautomove.unregister_mover(src)
+
+/// Registers an atom with SSautomove, including a move handler and metadata. Moving will begin next tick.
+/atom/movable/proc/start_automove(target, movement_type, datum/automove_metadata/metadata)
+ SHOULD_CALL_PARENT(TRUE)
+ SSautomove.register_mover(src, (movement_type || get_default_automove_controller_type()), metadata)
+
+/// Called when an atom is within the acceptable parameters for not moving further (ideal range). Does not necessarily imply the atom has unregistered (see stop_automove()).
+/atom/movable/proc/finished_automove()
+ SHOULD_CALL_PARENT(TRUE)
+ return FALSE
+
+/// Called by SSautomove when an atom fails to move in circumstances where it would like to. As with finished_automove, does not imply unregistering from SSautomove.
+/atom/movable/proc/failed_automove()
+ SHOULD_CALL_PARENT(TRUE)
+ return FALSE
+
+// Jesus Christ why do I write such long proc names
+/// Used by some mobs to vary the acceptable distance from target when automoving.
+/atom/movable/proc/get_acceptable_automove_distance_from_target()
+ return 0
+
+/// Should return a reference to the current atom target.
+/atom/movable/proc/get_automove_target(datum/automove_metadata/metadata)
+ return null
+
+/// Generalized entrypoint for checking CanMove and such on /mob.
+/atom/movable/proc/can_do_automated_move(variant_move_delay)
+ return FALSE
diff --git a/code/datums/movement/automove_controller.dm b/code/datums/movement/automove_controller.dm
new file mode 100644
index 00000000000..08dae443f35
--- /dev/null
+++ b/code/datums/movement/automove_controller.dm
@@ -0,0 +1,63 @@
+/// Implements automove logic; can be overridden on mob procs if you want to vary the logic from the below.
+/decl/automove_controller
+ var/completion_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom reaches its target.
+ var/failure_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom fails to move.
+
+/decl/automove_controller/proc/handle_mover(atom/movable/mover, datum/automove_metadata/metadata)
+
+ // Cease automovement if we got an invalid mover..
+ if(!istype(mover))
+ return PROCESS_KILL
+
+ // Null target means abandon pathing, regardless of return signals.
+ var/atom/target = mover.get_automove_target(metadata)
+ if(!istype(target))
+ return PROCESS_KILL
+
+ // Return early if we are in the process of moving, as we will definitely fail MayMove at the end()
+ if(ismob(mover))
+ var/mob/mover_mob = mover
+ if(mover_mob.moving)
+ return TRUE
+
+ // Cease automovement if we're already at the target.
+ var/avoid_target = metadata?.avoid_target
+ if(!avoid_target && (get_turf(mover) == get_turf(target) || (ismovable(target) && mover.Adjacent(target))))
+ mover.finished_automove()
+ return completion_signal
+
+ // Cease movement if we're close enough to the target.
+ var/acceptable_move_dist = isnull(metadata?.acceptable_distance) ? mover.get_acceptable_automove_distance_from_target() : metadata.acceptable_distance
+ if(avoid_target ? (get_dist(mover, target) >= acceptable_move_dist) : (get_dist(mover, target) <= acceptable_move_dist))
+ mover.finished_automove()
+ return completion_signal
+
+ // Cease automovement if we failed to move a turf.
+ if(mover.can_do_automated_move(metadata?.move_delay))
+ if(avoid_target)
+ target = get_edge_target_turf(target, get_dir(target, mover))
+
+ // Note for future coders: SelfMove() only confirms if a handler handled the move, not if the atom moved.
+ var/old_loc = mover.loc
+
+ // Try to move directly.
+ var/target_dir = get_dir(mover, target)
+ if(!target_dir)
+ if(avoid_target)
+ target_dir = pick(global.cardinal)
+ else
+ return TRUE // no idea how we would get into this position
+
+ if(mover.SelfMove(target_dir) && (old_loc != mover.loc))
+ return TRUE
+
+ // Try to move around any obstacle.
+ var/static/list/_alt_dir_rot = list(45, -45)
+ for(var/alt_dir in shuffle(_alt_dir_rot))
+ mover.reset_movement_delay()
+ if(mover.SelfMove(turn(target_dir, alt_dir)) && (old_loc != mover.loc))
+ return TRUE
+
+ mover.failed_automove()
+
+ return failure_signal
diff --git a/code/datums/movement/automove_metadata.dm b/code/datums/movement/automove_metadata.dm
new file mode 100644
index 00000000000..f1f4796cafb
--- /dev/null
+++ b/code/datums/movement/automove_metadata.dm
@@ -0,0 +1,10 @@
+// Overrides some aspects of mob movement for the purposes of automove.
+/datum/automove_metadata
+ var/move_delay
+ var/acceptable_distance
+ var/avoid_target
+
+/datum/automove_metadata/New(_move_delay, _acceptable_distance, _avoid_target)
+ move_delay = _move_delay
+ acceptable_distance = _acceptable_distance
+ avoid_target = _avoid_target
diff --git a/code/datums/movement/mob.dm b/code/datums/movement/mob.dm
index a059a123adf..8a2c5dfd130 100644
--- a/code/datums/movement/mob.dm
+++ b/code/datums/movement/mob.dm
@@ -165,7 +165,7 @@
return MOVEMENT_PROCEED
-// Finally.. the last of the mob movement junk
+// Finally... the last of the mob movement junk
/datum/movement_handler/mob/movement/DoMove(var/direction, var/mob/mover)
. = MOVEMENT_HANDLED
diff --git a/code/datums/music_tracks/elibao.dm b/code/datums/music_tracks/elibao.dm
index 821e23c85fd..72be0da4562 100644
--- a/code/datums/music_tracks/elibao.dm
+++ b/code/datums/music_tracks/elibao.dm
@@ -1,16 +1,16 @@
/decl/music_track/elibao
- artist = "Earthcrusher"
+ artist = "Sunbeamstress"
title = "every light is blinking at once"
song = 'sound/music/elibao.ogg'
license = /decl/license/cc_by_nc_sa_3_0
- url = "https://soundcloud.com/alexanderdivine/every-light-is-blinking-at-once"
+ url = "https://soundcloud.com/sunbeamstress/every-light-is-blinking-at-once"
/*
'every light is blinking at once'
-An original jam, (c)2018 Earthcrusher, aka Alexander Divine.
+An original jam, (c)2018 Sunbeamstress, aka Lauren Loveless.
Licensed for use under Creative Commons License: CC BY-SA 3.0
Use it however you like. Stay beautiful.
-Link to online version at: https://soundcloud.com/alexanderdivine/every-light-is-blinking-at-once
+Link to online version at: https://soundcloud.com/sunbeamstress/every-light-is-blinking-at-once
*/
diff --git a/code/datums/music_tracks/lasers.dm b/code/datums/music_tracks/lasers.dm
index 2811a92e344..940196b9695 100644
--- a/code/datums/music_tracks/lasers.dm
+++ b/code/datums/music_tracks/lasers.dm
@@ -1,16 +1,16 @@
/decl/music_track/lasers
- artist = "Earthcrusher"
+ artist = "Sunbeamstress"
title = "lasers rip apart the bulkhead"
song = 'sound/music/lasers_rip_apart_the_bulkhead.ogg'
license = /decl/license/cc_by_nc_sa_3_0
- url = "https://soundcloud.com/alexanderdivine/lasers-rip-apart-the-bulkhead"
+ url = "https://soundcloud.com/sunbeamstress/lasers-rip-apart-the-bulkhead"
/*
'lasers rip apart the bulkhead'
-An original jam, (c)2018 Earthcrusher, aka Alexander Divine.
+An original jam, (c)2018 Sunbeamstress, aka Lauren Loveless.
Licensed for use under Creative Commons License: CC BY-SA 3.0
Use it however you like. Stay beautiful.
-Link to online version at: https://soundcloud.com/alexanderdivine/lasers-rip-apart-the-bulkhead
+Link to online version at: https://soundcloud.com/sunbeamstress/lasers-rip-apart-the-bulkhead
*/
diff --git a/code/datums/music_tracks/pwmur.dm b/code/datums/music_tracks/pwmur.dm
index d3e1a66df06..bf917c86455 100644
--- a/code/datums/music_tracks/pwmur.dm
+++ b/code/datums/music_tracks/pwmur.dm
@@ -1,16 +1,16 @@
/decl/music_track/pwmur
- artist = "Earthcrusher"
+ artist = "Sunbeamstress"
title = "phoron will make us rich"
song = 'sound/music/pwmur.ogg'
license = /decl/license/cc_by_nc_sa_3_0
- url = "https://soundcloud.com/alexanderdivine/phoron-will-make-us-rich"
+ url = "https://soundcloud.com/sunbeamstress/phoron-will-make-us-rich"
/*
'phoron will make us rich'
-An original jam, (c)2018 Earthcrusher, aka Alexander Divine.
+An original jam, (c)2018 Sunbeamstress, aka Lauren Loveless.
Licensed for use under Creative Commons License: CC BY-SA 3.0
Use it however you like. Stay beautiful.
-Link to online version at: https://soundcloud.com/alexanderdivine/phoron-will-make-us-rich
+Link to online version at: https://soundcloud.com/sunbeamstress/phoron-will-make-us-rich
*/
diff --git a/code/datums/outfits/horror_killers.dm b/code/datums/outfits/horror_killers.dm
index 81a32bd7b7a..09b462349cc 100644
--- a/code/datums/outfits/horror_killers.dm
+++ b/code/datums/outfits/horror_killers.dm
@@ -34,7 +34,7 @@
var/victim = get_mannequin(H.ckey)
if(victim)
for(var/obj/item/carried_item in H.get_equipped_items(TRUE))
- carried_item.add_blood(victim) //Oh yes, there will be blood.. just not blood from the killer because that's odd
+ carried_item.add_blood(victim) //Oh yes, there will be blood... just not blood from the killer because that's odd
/decl/hierarchy/outfit/reaper
name = "Reaper"
diff --git a/code/datums/repositories/client.dm b/code/datums/repositories/client.dm
index 386fffcfc66..44c247c2073 100644
--- a/code/datums/repositories/client.dm
+++ b/code/datums/repositories/client.dm
@@ -9,7 +9,7 @@ var/global/repository/client/client_repository = new()
..()
clients_ = list()
-// A lite client is unique per ckey and mob ref (save for ref conflicts.. oh well)
+// A lite client is unique per ckey and mob ref (save for ref conflicts... oh well)
/repository/client/proc/get_lite_client(var/mob/M)
if(isclient(M))
var/client/C = M // BYOND is supposed to ensure clients always have a mob
diff --git a/code/datums/repositories/mobs.dm b/code/datums/repositories/mobs.dm
index 024aed9c450..87af6ea7cac 100644
--- a/code/datums/repositories/mobs.dm
+++ b/code/datums/repositories/mobs.dm
@@ -7,7 +7,7 @@ var/global/repository/mob/mob_repository = new()
..()
mobs_ = list()
-// A lite mob is unique per ckey and mob real name/ref (ref conflicts possible.. but oh well)
+// A lite mob is unique per ckey and mob real name/ref (ref conflicts possible... but oh well)
/repository/mob/proc/get_lite_mob(var/mob/M)
. = mobs_[mob2unique(M)]
if(!.)
diff --git a/code/game/atoms_init.dm b/code/game/atoms_init.dm
index f072d4d7be1..dcbd9a7ba2c 100644
--- a/code/game/atoms_init.dm
+++ b/code/game/atoms_init.dm
@@ -158,7 +158,7 @@
return list(loc)
/atom/PopulateClone(atom/clone)
- //Not entirely sure about icon stuff. Some legacy things would need it copied, but not more recently coded atoms..
+ //Not entirely sure about icon stuff. Some legacy things would need it copied, but not more recently coded atoms.
clone.appearance = appearance
clone.set_invisibility(invisibility)
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 13c6665f9b4..50ffbccd510 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -562,3 +562,8 @@
/atom/movable/proc/end_throw()
throwing = null
+
+/atom/movable/proc/reset_movement_delay()
+ var/datum/movement_handler/delay/delay = locate() in movement_handlers
+ if(istype(delay))
+ delay.next_move = world.time
diff --git a/code/game/machinery/_machines_base/machinery_damage.dm b/code/game/machinery/_machines_base/machinery_damage.dm
index 96b81329d5a..2ecf6cd43e8 100644
--- a/code/game/machinery/_machines_base/machinery_damage.dm
+++ b/code/game/machinery/_machines_base/machinery_damage.dm
@@ -83,7 +83,7 @@
take_damage(P.damage, P.atom_damage_type)
/obj/machinery/bash(obj/item/W, mob/user)
- if(!istype(W) || W.force <= 5 || (W.item_flags & ITEM_FLAG_NO_BLUDGEON))
+ if(!istype(W) || W.force <= 5)
return FALSE
. = ..()
if(.)
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index 890d9653e03..411990a7465 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -779,14 +779,16 @@
apply_danger_level(danger_level)
update_icon()
-/obj/machinery/alarm/attackby(obj/item/W, mob/user)
+/obj/machinery/alarm/attackby(obj/item/used_item, mob/user)
if(!(stat & (BROKEN|NOPOWER)))
- if (istype(W, /obj/item/card/id) || istype(W, /obj/item/modular_computer))// trying to unlock the interface with an ID card
+ if (istype(used_item, /obj/item/card/id) || istype(used_item, /obj/item/modular_computer))// trying to unlock the interface with an ID card
+ if(!used_item.user_can_wield(user))
+ return TRUE
if(allowed(user) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN))
locked = !locked
- to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.")
+ to_chat(user, SPAN_NOTICE("You [ locked ? "lock" : "unlock"] the Air Alarm interface."))
else
- to_chat(user, "Access denied.")
+ to_chat(user, SPAN_WARNING("Access denied."))
return TRUE
return ..()
diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm
index 69f4e4de4ab..e81ec84863a 100644
--- a/code/game/machinery/computer/message.dm
+++ b/code/game/machinery/computer/message.dm
@@ -54,7 +54,7 @@
/obj/machinery/computer/message_monitor/emag_act(var/remaining_charges, var/mob/user)
// Will create sparks and print out the console's password. You will then have to wait a while for the console to be back online.
- // It'll take more time if there's more characters in the password..
+ // It'll take more time if there's more characters in the password.
if(!emag && operable())
var/obj/machinery/network/message_server/linked_server = get_message_server()
if(linked_server)
diff --git a/code/game/machinery/doors/_door.dm b/code/game/machinery/doors/_door.dm
index 3af28af67b7..7d26dbd2645 100644
--- a/code/game/machinery/doors/_door.dm
+++ b/code/game/machinery/doors/_door.dm
@@ -330,18 +330,24 @@
emagged = TRUE
return 1
-/obj/machinery/door/bash(obj/item/I, mob/user)
- if(density && user.a_intent == I_HURT && !(I.item_flags & ITEM_FLAG_NO_BLUDGEON))
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
- user.do_attack_animation(src)
- if(I.force < min_force)
- user.visible_message("\The [user] hits \the [src] with \the [I] with no visible effect.")
- else
- user.visible_message("\The [user] forcefully strikes \the [src] with \the [I]!")
- playsound(src.loc, hitsound, 100, 1)
- take_damage(I.force, I.atom_damage_type)
- return TRUE
- return FALSE
+/obj/machinery/door/bash(obj/item/weapon, mob/user)
+ if(isliving(user) && user.a_intent != I_HURT)
+ return FALSE
+ if(!weapon.user_can_wield(user))
+ return FALSE
+ if(weapon.item_flags & ITEM_FLAG_NO_BLUDGEON)
+ return FALSE
+ if(!density)
+ return FALSE
+ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.do_attack_animation(src)
+ if(weapon.force < min_force)
+ user.visible_message("\The [user] hits \the [src] with \the [weapon] with no visible effect.")
+ else
+ user.visible_message("\The [user] forcefully strikes \the [src] with \the [weapon]!")
+ playsound(src.loc, hitsound, 100, 1)
+ take_damage(weapon.force, weapon.atom_damage_type)
+ return TRUE
/obj/machinery/door/take_damage(damage, damage_type = BRUTE, damage_flags, inflicter, armor_pen = 0, silent, do_update_health)
if(!current_health)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 7bb1bb09480..891c8b3c96b 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -813,20 +813,20 @@ About the new airlock wires panel:
else
return ..()
-/obj/machinery/door/airlock/bash(obj/item/I, mob/user)
- //if door is unbroken, hit with fire axe using harm intent
- if (istype(I, /obj/item/twohanded/fireaxe) && !(stat & BROKEN) && user.a_intent == I_HURT)
+/obj/machinery/door/airlock/bash(obj/item/weapon, mob/user)
+ //if door is unbroken, hit with fire axe using harm intent
+ if (istype(weapon, /obj/item/twohanded/fireaxe) && !(stat & BROKEN) && user.a_intent == I_HURT && weapon.user_can_wield(user))
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
- var/obj/item/twohanded/fireaxe/F = I
+ var/obj/item/twohanded/fireaxe/F = weapon
if (F.wielded)
playsound(src, 'sound/weapons/smash.ogg', 100, 1)
current_health -= F.force_wielded * 2
if(current_health <= 0)
- user.visible_message(SPAN_DANGER("[user] smashes \the [I] into the airlock's control panel! It explodes in a shower of sparks!"), SPAN_DANGER("You smash \the [I] into the airlock's control panel! It explodes in a shower of sparks!"))
+ user.visible_message(SPAN_DANGER("[user] smashes \the [weapon] into the airlock's control panel! It explodes in a shower of sparks!"), SPAN_DANGER("You smash \the [weapon] into the airlock's control panel! It explodes in a shower of sparks!"))
current_health = 0
set_broken(TRUE)
else
- user.visible_message(SPAN_DANGER("[user] smashes \the [I] into the airlock's control panel!"))
+ user.visible_message(SPAN_DANGER("[user] smashes \the [weapon] into the airlock's control panel!"))
return TRUE
return ..()
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 66171dc8f41..722d253c526 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -197,15 +197,15 @@
else if (density)
flick("[base_state]deny", src)
-/obj/machinery/door/window/bash(obj/item/I, mob/user)
+/obj/machinery/door/window/bash(obj/item/weapon, mob/user)
//Emags and energy swords? You may pass.
- if (istype(I, /obj/item/energy_blade))
- var/obj/item/energy_blade/blade = I
+ if (weapon.user_can_wield(user) && istype(weapon, /obj/item/energy_blade))
+ var/obj/item/energy_blade/blade = weapon
if(blade.is_special_cutting_tool() && emag_act(10, user))
spark_at(loc, amount=5)
playsound(loc, 'sound/weapons/blade1.ogg', 50, 1)
visible_message(SPAN_WARNING("The glass door was sliced open by [user]!"))
- return 1
+ return TRUE
return ..()
/obj/machinery/door/window/brigdoor
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 07afed57749..1e7c55f96dc 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -146,7 +146,7 @@ var/global/list/holopads = list()
to_chat(user, "Using such sophisticated technology, just to talk to yourself seems a bit silly.")
return
if(targetpad && targetpad.caller_id)
- to_chat(user, "The pad flashes a busy sign. Maybe you should try again later..")
+ to_chat(user, "The pad flashes a busy sign. Maybe you should try again later.")
return
if(targetpad)
make_call(targetpad, user)
diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm
index 99da01b0b81..d6ef1ab8daa 100644
--- a/code/game/machinery/jukebox.dm
+++ b/code/game/machinery/jukebox.dm
@@ -143,7 +143,6 @@
return TRUE
/obj/machinery/media/jukebox/proc/explode()
- walk_to(src,0)
src.visible_message("\the [src] blows apart!", 1)
explosion(src.loc, 0, 0, 1, rand(1,2), 1)
diff --git a/code/game/machinery/vending/food.dm b/code/game/machinery/vending/food.dm
index cb832d9b56d..65f1bc4428e 100644
--- a/code/game/machinery/vending/food.dm
+++ b/code/game/machinery/vending/food.dm
@@ -26,7 +26,7 @@
/obj/item/chems/food/syndicake = 6
)
-//a food variant of the boda machine - It carries slavic themed foods.. Mostly beer snacks
+//a food variant of the boda machine - It carries slavic themed foods. Mostly beer snacks
/obj/machinery/vending/snix
name = "Snix"
desc = "An old snack vending machine, how did it get here? And are the snacks still good?"
diff --git a/code/game/machinery/wall_frames.dm b/code/game/machinery/wall_frames.dm
index 17214a15e77..f3f7fb0ed89 100644
--- a/code/game/machinery/wall_frames.dm
+++ b/code/game/machinery/wall_frames.dm
@@ -303,7 +303,7 @@
/obj/item/frame/button/airlock_controller/kit/warn_not_setup(mob/user)
to_chat(user, SPAN_WARNING("First, use a multitool on the kit to properly setup the controller's software!"))
-//Let them also hit it with a circuitboard if they so wish. But multitool is better when you don't want to print one for nothing..
+//Let them also hit it with a circuitboard if they so wish. But multitool is better when you don't want to print one for nothing.
/obj/item/frame/button/airlock_controller/kit/attackby(obj/item/W, mob/user)
if(!IS_MULTITOOL(W))
return ..()
diff --git a/code/game/objects/effects/decals/posters/bs12.dm b/code/game/objects/effects/decals/posters/bs12.dm
index b02e172a875..6a585fa4e93 100644
--- a/code/game/objects/effects/decals/posters/bs12.dm
+++ b/code/game/objects/effects/decals/posters/bs12.dm
@@ -49,7 +49,7 @@ DEFINE_POSTER( bay_43, "bsposter43", "Responsible medbay habits, No #3",
DEFINE_POSTER( bay_45, "bsposter45", "Responsible engineering habits, No #1", "A safety poster featuring a blue haired engineer. \"When repairing a machine or construction, always aim for long-term solutions.\"")
DEFINE_POSTER( bay_46, "bsposter46", "Inspirational lawyer", "An inspirational poster depicting an alien lawyer. He seems to be shouting something, while pointing fiercely to the right.")
DEFINE_POSTER( bay_47, "bsposter47", "Security pinup", "This is a pin-up poster. A dark skinned white haired girl poses in the sunlight wearing a tank top with her stomach exposed. The text on the poster states \"M, Succubus of Security.\" and a lipstick mark stains the top right corner, as if kissed by the model herself.")
-DEFINE_POSTER( bay_48, "bsposter48", "Borg pinup?", "This is a.. pin-up poster? It is a diagram on an old model of cyborg with a note scribbled in marker on the bottom, on the top there is a large XO written in red marker.")
+DEFINE_POSTER( bay_48, "bsposter48", "Borg pinup?", "This is a... pin-up poster? It is a diagram of an old model of cyborg with a note scribbled in marker on the bottom, on the top there is a large XO written in red marker.")
DEFINE_POSTER( bay_49, "bsposter49", "Engineering recruitment", "This is a poster showing an engineer relaxing by a computer, the text states \"Living the life! Join Engineering today!\"")
DEFINE_POSTER( bay_50, "bsposter50", "Pinup Girl Cindy Kate", "This particular one is of Cindy Kate, a seductive performer well known among less savoury circles.")
DEFINE_POSTER( bay_51, "bsposter51", "space appreciation poster", "This is a poster produced by the Generic Space Company, as a part of a series of commemorative posters on the wonders of space. One of three.")
diff --git a/code/game/objects/effects/fake_fire.dm b/code/game/objects/effects/fake_fire.dm
index d3527872dd9..783ecd3ac62 100644
--- a/code/game/objects/effects/fake_fire.dm
+++ b/code/game/objects/effects/fake_fire.dm
@@ -5,15 +5,30 @@
layer = FIRE_LAYER
var/lifetime = 10 SECONDS //0 for infinite
//See Fire.dm (the real one), but in a nutshell:
- var/firelevel = 0 //Larger the number, worse burns.
- var/last_temperature = 0 //People with heat protection above this temp will be immune.
- var/pressure = 0 //Larger the number, worse burns.
+ var/firelevel = 1 //Larger the number, worse burns.
+ var/last_temperature = T100C //People with heat protection above this temp will be immune.
+ var/pressure = ONE_ATMOSPHERE //Larger the number, worse burns.
/obj/effect/fake_fire/Initialize()
. = ..()
if(!loc)
return INITIALIZE_HINT_QDEL
- set_light(3, 0.5, color)
+ var/new_light_range
+ var/new_light_power
+ if(firelevel > 6)
+ icon_state = "3"
+ new_light_range = 7
+ new_light_power = 3
+ else if(firelevel > 2.5)
+ icon_state = "2"
+ new_light_range = 5
+ new_light_power = 2
+ else
+ icon_state = "1"
+ new_light_range = 3
+ new_light_power = 1
+ color = fire_color()
+ set_light(new_light_range, new_light_power, color)
START_PROCESSING(SSobj,src)
if(lifetime)
QDEL_IN(src,lifetime)
@@ -22,11 +37,19 @@
if(!loc)
qdel(src)
return PROCESS_KILL
- for(var/mob/living/L in loc)
- L.FireBurn(firelevel,last_temperature,pressure)
- loc.fire_act(firelevel,last_temperature,pressure)
- for(var/atom/A in loc)
- A.fire_act(firelevel,last_temperature,pressure)
+ for(var/mob/living/victim in loc)
+ if(!victim.simulated)
+ continue
+ victim.FireBurn(firelevel, last_temperature, pressure)
+ loc.fire_act(firelevel, last_temperature, pressure)
+ for(var/atom/burned in loc)
+ if(!burned.simulated || burned == src)
+ continue
+ burned.fire_act(firelevel, last_temperature, pressure)
+
+/obj/effect/fake_fire/proc/fire_color()
+ var/temperature = max(4000*sqrt(firelevel/vsc.fire_firelevel_multiplier), last_temperature)
+ return heat2color(temperature)
/obj/effect/fake_fire/Destroy()
STOP_PROCESSING(SSobj,src)
diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm
index 7618ae80ae3..d529f6d9b30 100644
--- a/code/game/objects/items/__item.dm
+++ b/code/game/objects/items/__item.dm
@@ -521,26 +521,31 @@
return TRUE
return FALSE
-/obj/item/attackby(obj/item/W, mob/user)
+/obj/item/proc/user_can_wield(mob/user, silent = FALSE)
+ return !needs_attack_dexterity || user.check_dexterity(needs_attack_dexterity, silent = silent)
- if(try_slapcrafting(W, user))
+/obj/item/attackby(obj/item/used_item, mob/user)
+ // if can_wield is false we still need to call parent for storage objects to work properly
+ var/can_wield = user_can_wield(user, silent = TRUE)
+
+ if(can_wield && try_slapcrafting(used_item, user))
return TRUE
- if(W.storage?.use_to_pickup)
+ if(used_item.storage?.use_to_pickup)
//Mode is set to collect all items
- if(W.storage.collection_mode && isturf(src.loc))
- W.storage.gather_all(src.loc, user)
+ if(used_item.storage.collection_mode && isturf(src.loc))
+ used_item.storage.gather_all(src.loc, user)
return TRUE
- if(W.storage.can_be_inserted(src, user))
- W.storage.handle_item_insertion(user, src)
+ if(used_item.storage.can_be_inserted(src, user))
+ used_item.storage.handle_item_insertion(user, src)
return TRUE
- if(has_extension(src, /datum/extension/loaded_cell))
+ if(can_wield && has_extension(src, /datum/extension/loaded_cell))
var/datum/extension/loaded_cell/cell_loaded = get_extension(src, /datum/extension/loaded_cell)
- if(cell_loaded.has_tool_unload_interaction(W))
- return cell_loaded.try_unload(user, W)
- else if(istype(W, /obj/item/cell))
- return cell_loaded.try_load(user, W)
+ if(cell_loaded.has_tool_unload_interaction(used_item))
+ return cell_loaded.try_unload(user, used_item)
+ else if(istype(used_item, /obj/item/cell))
+ return cell_loaded.try_load(user, used_item)
return ..()
diff --git a/code/game/objects/items/cryobag.dm b/code/game/objects/items/cryobag.dm
index 5d2a0699802..b9dd96121a5 100644
--- a/code/game/objects/items/cryobag.dm
+++ b/code/game/objects/items/cryobag.dm
@@ -111,7 +111,7 @@
/obj/item/usedcryobag
name = "used stasis bag"
- desc = "Pretty useless now.."
+ desc = "Pretty useless now..."
icon_state = "bodybag_used"
icon = 'icons/obj/closets/cryobag.dmi'
material = /decl/material/solid/organic/plastic
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 42c0a126138..fc4f397ad3f 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -86,6 +86,8 @@
I.maptext_y = 2
I.maptext = STYLE_SMALLFONTS_OUTLINE(get_amount(), 6, (color || COLOR_WHITE), COLOR_BLACK)
add_overlay(I)
+ else
+ compile_overlays() // prevent maptext from showing when we're dropped
/obj/item/stack/Move()
var/on_turf = isturf(loc)
@@ -402,7 +404,7 @@
continue
var/transfer = transfer_to(item)
if(user && transfer)
- to_chat(user, SPAN_NOTICE("You add a new [item.singular_name] to the stack. It now contains [item.amount] [item.singular_name]\s."))
+ to_chat(user, SPAN_NOTICE("You add [item.get_string_for_amount(transfer)] to the stack. It now contains [item.amount] [item.singular_name]\s."))
if(!amount)
break
return !QDELETED(src)
@@ -451,7 +453,7 @@
/**Whether a stack has the capability to be split. */
/obj/item/stack/proc/can_split()
- return !(uses_charge && !force) //#TODO: The !force was a hacky way to tell if its a borg or rigsuit module. Probably would be good to find a better way..
+ return !(uses_charge && !force) //#TODO: The !force was a hacky way to tell if its a borg or rigsuit module. Probably would be good to find a better way...
/**Whether a stack type has the capability to be merged. */
/obj/item/stack/proc/can_merge_stacks(var/obj/item/stack/other)
diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm
index 1aef86677a8..26ad711148e 100644
--- a/code/game/objects/items/weapons/grenades/flashbang.dm
+++ b/code/game/objects/items/weapons/grenades/flashbang.dm
@@ -80,10 +80,6 @@
if(!ear_safety)
sound_to(M, 'sound/weapons/flash_ring.ogg')
-/obj/item/grenade/flashbang/Destroy()
- walk(src, 0) // Because we might have called walk_away, we must stop the walk loop or BYOND keeps an internal reference to us forever.
- return ..()
-
/obj/item/grenade/flashbang/instant
invisibility = INVISIBILITY_MAXIMUM
is_spawnable_type = FALSE // Do not manually spawn this, it will runtime/break.
@@ -123,10 +119,10 @@
. = ..() //Segments should never exist except part of the clusterbang, since these immediately 'do their thing' and asplode
banglet = 1
activate()
- var/stepdist = rand(1,4)//How far to step
- var/temploc = src.loc//Saves the current location to know where to step away from
- walk_away(src,temploc,stepdist)//I must go, my people need me
+ //I must go, my people need me
addtimer(CALLBACK(src, PROC_REF(detonate)), rand(15,60))
+ if(isturf(loc)) // Don't hurl yourself around if you're not on a turf.
+ throw_at(get_edge_target_turf(loc, pick(global.cardinal)), rand(1, 4), 5, null, TRUE)
/obj/item/grenade/flashbang/clusterbang/segment/detonate()
var/numspawned = rand(4,8)
@@ -142,7 +138,6 @@
. = ..() //Same concept as the segments, so that all of the parts don't become reliant on the clusterbang
banglet = 1
activate()
- var/stepdist = rand(1,3)
- var/temploc = src.loc
- walk_away(src,temploc,stepdist)
addtimer(CALLBACK(src, PROC_REF(detonate)), rand(15,60))
+ if(isturf(loc)) // See Initialize() for above.
+ throw_at(get_edge_target_turf(loc, pick(global.cardinal)), rand(1, 3), 5, null, TRUE)
diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm
index 763eb23c7d8..d16532308ee 100644
--- a/code/game/objects/items/weapons/grenades/grenade.dm
+++ b/code/game/objects/items/weapons/grenades/grenade.dm
@@ -60,6 +60,8 @@
/obj/item/grenade/attack_self(mob/user)
if(active)
return
+ if(!user.check_dexterity(DEXTERITY_WEAPONS))
+ return TRUE // prevent further interactions
if(clown_check(user))
to_chat(user, "You prime \the [name]! [det_time/10] seconds!")
activate(user)
diff --git a/code/game/objects/items/weapons/storage/fancy/cigarettes.dm b/code/game/objects/items/weapons/storage/fancy/cigarettes.dm
index d83391c5722..10d4474afa3 100644
--- a/code/game/objects/items/weapons/storage/fancy/cigarettes.dm
+++ b/code/game/objects/items/weapons/storage/fancy/cigarettes.dm
@@ -79,7 +79,7 @@
/obj/item/box/fancy/cigarettes/killthroat/populate_reagents()
add_to_reagents(/decl/material/liquid/fuel, (max(1, storage?.max_storage_space) * 4))
-// New exciting ways to kill your lungs! - Earthcrusher //
+// New exciting ways to kill your lungs! - Sunbeamstress //
/obj/item/box/fancy/cigarettes/luckystars
name = "pack of Lucky Stars"
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index 0c131d9bce3..4b5c31597b8 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -168,7 +168,7 @@
if (R)
return ..()
else // Stop pretending and get out of your cardborg suit, human.
- to_chat(user, "You don't seem to be able to interact with this by yourself..")
+ to_chat(user, "You don't seem to be able to interact with this by yourself.")
add_fingerprint(user)
return 0
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 767b6907fb7..8ae8315d01e 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -128,11 +128,13 @@
if(atom_damage_type == BURN)
. |= DAM_LASER
-/obj/attackby(obj/item/O, mob/user)
- if((obj_flags & OBJ_FLAG_ANCHORABLE) && (IS_WRENCH(O) || IS_HAMMER(O)))
- wrench_floor_bolts(user, null, O)
- update_icon()
- return TRUE
+/obj/attackby(obj/item/used_item, mob/user)
+ // We need to call parent even if we lack dexterity, so that storage can work.
+ if(used_item.user_can_wield(user))
+ if((obj_flags & OBJ_FLAG_ANCHORABLE) && (IS_WRENCH(used_item) || IS_HAMMER(used_item)))
+ wrench_floor_bolts(user, null, used_item)
+ update_icon()
+ return TRUE
return ..()
/obj/proc/wrench_floor_bolts(mob/user, delay = 2 SECONDS, obj/item/tool)
diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm
index 5d5d9fb88ce..423b8d07818 100644
--- a/code/game/objects/structures/__structure.dm
+++ b/code/game/objects/structures/__structure.dm
@@ -10,7 +10,7 @@
var/structure_flags
var/last_damage_message
- var/hitsound = 'sound/weapons/smash.ogg'
+ var/hitsound = 'sound/weapons/Genhit.ogg'
var/parts_type
var/parts_amount
var/footstep_type
@@ -61,7 +61,7 @@
reinf_material = GET_DECL(reinf_material)
. = ..()
update_materials()
- if(lock)
+ if(lock && !istype(loc))
lock = new /datum/lock(src, lock)
if(!CanFluidPass())
fluid_update(TRUE)
diff --git a/code/game/objects/structures/_structure_construction.dm b/code/game/objects/structures/_structure_construction.dm
index 41024b79221..857d097af5e 100644
--- a/code/game/objects/structures/_structure_construction.dm
+++ b/code/game/objects/structures/_structure_construction.dm
@@ -113,32 +113,32 @@
last_damage_message = null
current_health = clamp(current_health + used*DOOR_REPAIR_AMOUNT, current_health, current_max_health)
-/obj/structure/attackby(obj/item/O, mob/user)
+/obj/structure/attackby(obj/item/used_item, mob/user)
+ if(used_item.user_can_wield(user))
+ if(used_item.force && user.a_intent == I_HURT)
+ attack_animation(user)
+ visible_message(SPAN_DANGER("\The [src] has been [pick(used_item.attack_verb)] with \the [used_item] by \the [user]!"))
+ take_damage(used_item.force, used_item.atom_damage_type)
+ . = TRUE
- if(O.force && user.a_intent == I_HURT)
- attack_animation(user)
- visible_message(SPAN_DANGER("\The [src] has been [pick(O.attack_verb)] with \the [O] by \the [user]!"))
- take_damage(O.force, O.atom_damage_type)
- . = TRUE
-
- else if(IS_WRENCH(O))
- . = handle_default_wrench_attackby(user, O)
- else if(IS_SCREWDRIVER(O))
- . = handle_default_screwdriver_attackby(user, O)
- else if(IS_WELDER(O))
- . = handle_default_welder_attackby(user, O)
- else if(IS_CROWBAR(O))
- . = handle_default_crowbar_attackby(user, O)
- else if(IS_COIL(O))
- . = handle_default_cable_attackby(user, O)
- else if(IS_WIRECUTTER(O))
- . = handle_default_wirecutter_attackby(user, O)
- else if(can_repair_with(O) && can_repair(user))
- . = handle_repair(user, O)
- if(.)
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
- add_fingerprint(user)
- return
+ else if(IS_WRENCH(used_item))
+ . = handle_default_wrench_attackby(user, used_item)
+ else if(IS_SCREWDRIVER(used_item))
+ . = handle_default_screwdriver_attackby(user, used_item)
+ else if(IS_WELDER(used_item))
+ . = handle_default_welder_attackby(user, used_item)
+ else if(IS_CROWBAR(used_item))
+ . = handle_default_crowbar_attackby(user, used_item)
+ else if(IS_COIL(used_item))
+ . = handle_default_cable_attackby(user, used_item)
+ else if(IS_WIRECUTTER(used_item))
+ . = handle_default_wirecutter_attackby(user, used_item)
+ else if(can_repair_with(used_item) && can_repair(user))
+ . = handle_repair(user, used_item)
+ if(.)
+ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ add_fingerprint(user)
+ return .
return ..()
/obj/structure/attack_generic(var/mob/user, var/damage, var/attack_verb, var/environment_smash)
diff --git a/code/game/objects/structures/crates_lockers/closets/__closet.dm b/code/game/objects/structures/crates_lockers/closets/__closet.dm
index 6e4f6f50bb5..d1b0f8f37ba 100644
--- a/code/game/objects/structures/crates_lockers/closets/__closet.dm
+++ b/code/game/objects/structures/crates_lockers/closets/__closet.dm
@@ -244,69 +244,74 @@ var/global/list/closets = list()
..()
take_damage(proj_damage, Proj.atom_damage_type)
-/obj/structure/closet/attackby(obj/item/W, mob/user)
+/obj/structure/closet/attackby(obj/item/used_item, mob/user)
- if(user.a_intent == I_HURT && W.force)
+ if(user.a_intent == I_HURT && used_item.force)
return ..()
- if(!opened && (istype(W, /obj/item/stack/material) || IS_WRENCH(W)) )
+ if(!opened && (istype(used_item, /obj/item/stack/material) || IS_WRENCH(used_item)) )
return ..()
+ var/can_wield = used_item.user_can_wield(user, silent = TRUE)
+
if(opened)
- if(istype(W, /obj/item/grab))
- var/obj/item/grab/G = W
- receive_mouse_drop(G.affecting, user) //act like they were dragged onto the closet
- return TRUE
- if(IS_WELDER(W))
- var/obj/item/weldingtool/WT = W
- if(WT.weld(0,user))
- slice_into_parts(WT, user)
+ if(can_wield)
+ if(istype(used_item, /obj/item/grab))
+ var/obj/item/grab/G = used_item
+ receive_mouse_drop(G.affecting, user) //act like they were dragged onto the closet
+ return TRUE
+ if(IS_WELDER(used_item))
+ var/obj/item/weldingtool/WT = used_item
+ if(WT.weld(0,user))
+ slice_into_parts(WT, user)
+ return TRUE
+ if(istype(used_item, /obj/item/gun/energy/plasmacutter))
+ var/obj/item/gun/energy/plasmacutter/cutter = used_item
+ if(cutter.slice(user))
+ slice_into_parts(used_item, user)
+ return TRUE
+ if(istype(used_item, /obj/item/laundry_basket) && used_item.contents.len && used_item.storage)
+ var/turf/T = get_turf(src)
+ for(var/obj/item/I in used_item.storage.get_contents())
+ used_item.storage.remove_from_storage(user, I, T, TRUE)
+ used_item.storage.finish_bulk_removal()
+ user.visible_message(
+ SPAN_NOTICE("\The [user] empties \the [used_item] into \the [src]."),
+ SPAN_NOTICE("You empty \the [used_item] into \the [src]."),
+ SPAN_NOTICE("You hear rustling of clothes.")
+ )
return TRUE
- if(istype(W, /obj/item/gun/energy/plasmacutter))
- var/obj/item/gun/energy/plasmacutter/cutter = W
- if(cutter.slice(user))
- slice_into_parts(W, user)
- return TRUE
-
- if(istype(W, /obj/item/laundry_basket) && W.contents.len && W.storage)
- var/turf/T = get_turf(src)
- for(var/obj/item/I in W.storage.get_contents())
- W.storage.remove_from_storage(user, I, T, TRUE)
- W.storage.finish_bulk_removal()
- user.visible_message(
- SPAN_NOTICE("\The [user] empties \the [W] into \the [src]."),
- SPAN_NOTICE("You empty \the [W] into \the [src]."),
- SPAN_NOTICE("You hear rustling of clothes.")
- )
- return TRUE
- if(user.try_unequip(W, loc))
- W.pixel_x = 0
- W.pixel_y = 0
- W.pixel_z = 0
- W.pixel_w = 0
+ if(user.try_unequip(used_item, loc))
+ used_item.pixel_x = 0
+ used_item.pixel_y = 0
+ used_item.pixel_z = 0
+ used_item.pixel_w = 0
return TRUE
return FALSE
- if(try_key_unlock(W, user))
+ if(!can_wield)
+ return attack_hand_with_interaction_checks(user)
+
+ if(try_key_unlock(used_item, user))
return TRUE
- if(try_install_lock(W, user))
+ if(try_install_lock(used_item, user))
return TRUE
- if(istype(W, /obj/item/energy_blade))
- var/obj/item/energy_blade/blade = W
- if(blade.is_special_cutting_tool() && emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying."))
+ if(istype(used_item, /obj/item/energy_blade))
+ var/obj/item/energy_blade/blade = used_item
+ if(blade.is_special_cutting_tool() && emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [used_item]!", "You hear metal being sliced and sparks flying."))
spark_at(loc, amount=5)
playsound(loc, 'sound/weapons/blade1.ogg', 50, 1)
open(user)
return TRUE
- if(istype(W, /obj/item/stack/package_wrap))
+ if(istype(used_item, /obj/item/stack/package_wrap))
return FALSE //Return false to get afterattack to be called
- if(IS_WELDER(W) && (setup & CLOSET_CAN_BE_WELDED))
- var/obj/item/weldingtool/WT = W
+ if(IS_WELDER(used_item) && (setup & CLOSET_CAN_BE_WELDED))
+ var/obj/item/weldingtool/WT = used_item
if(!WT.weld(0,user))
if(WT.isOn())
to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task."))
@@ -316,7 +321,7 @@ var/global/list/closets = list()
user.visible_message(SPAN_WARNING("\The [src] has been [welded?"welded shut":"unwelded"] by \the [user]."), blind_message = "You hear welding.", range = 3)
return TRUE
else if(setup & CLOSET_HAS_LOCK)
- togglelock(user, W)
+ togglelock(user, used_item)
return TRUE
return attack_hand_with_interaction_checks(user)
diff --git a/code/game/objects/structures/doors/_door.dm b/code/game/objects/structures/doors/_door.dm
index 6655f3ba9d3..c590d90dee4 100644
--- a/code/game/objects/structures/doors/_door.dm
+++ b/code/game/objects/structures/doors/_door.dm
@@ -57,6 +57,9 @@
W.queue_icon_update()
for(var/turf/wall/W in RANGE_TURFS(loc, 1))
+ var/turf_dir = get_dir(loc, W)
+ if(turf_dir & (turf_dir - 1)) // if diagonal
+ continue // skip diagonals
set_dir(turn(get_dir(loc, W), 90))
break
@@ -136,17 +139,18 @@
/obj/structure/door/can_install_lock()
return TRUE
-/obj/structure/door/attackby(obj/item/I, mob/user)
- add_fingerprint(user, 0, I)
+/obj/structure/door/attackby(obj/item/used_item, mob/user)
+ add_fingerprint(user, 0, used_item)
- if((user.a_intent == I_HURT && I.force) || istype(I, /obj/item/stack/material))
+ if((user.a_intent == I_HURT && used_item.force) || istype(used_item, /obj/item/stack/material))
return ..()
- if(try_key_unlock(I, user))
- return TRUE
+ if(used_item.user_can_wield(user, silent = TRUE))
+ if(try_key_unlock(used_item, user))
+ return TRUE
- if(try_install_lock(I, user))
- return TRUE
+ if(try_install_lock(used_item, user))
+ return TRUE
if(density)
open(user)
diff --git a/code/game/objects/structures/flora/tree.dm b/code/game/objects/structures/flora/tree.dm
index fd1d4960d61..21a57d867fb 100644
--- a/code/game/objects/structures/flora/tree.dm
+++ b/code/game/objects/structures/flora/tree.dm
@@ -86,7 +86,7 @@
LAZYADD(., new log_type(T, rand(max(1,round(log_amount*0.5)), log_amount), material?.type, reinf_material?.type))
if(stump_type)
var/obj/structure/flora/stump/stump = new stump_type(T, material, reinf_material)
- stump.icon_state = icon_state //A bit dirty maybe, but its probably not worth writing a whole system for this when we have 3 kinds of trees..
+ stump.icon_state = icon_state //A bit dirty maybe, but its probably not worth writing a whole system for this when we have 3 kinds of trees...
if(paint_color)
stump.set_color()
. = ..()
diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm
index e4d5d1636b3..c997a7e0780 100644
--- a/code/game/objects/structures/signs.dm
+++ b/code/game/objects/structures/signs.dm
@@ -62,7 +62,7 @@
sign_type = null
return S
-///Attempts installing the sign and ask the user for direction and etc..
+///Attempts installing the sign and ask the user for direction and etc.
/obj/item/sign/proc/try_install(var/turf/targeted_turf, var/mob/user)
var/facing = get_cardinal_dir(user, targeted_turf) || user.dir
var/install_dir = global.reverse_dir[facing]
diff --git a/code/game/objects/structures/structure_lock.dm b/code/game/objects/structures/structure_lock.dm
index 7975ad07266..61d28e0766b 100644
--- a/code/game/objects/structures/structure_lock.dm
+++ b/code/game/objects/structures/structure_lock.dm
@@ -1,23 +1,25 @@
/obj/structure
var/datum/lock/lock
-/obj/structure/proc/try_key_unlock(obj/item/I, mob/user)
+/obj/structure/proc/try_key_unlock(obj/item/used_item, mob/user)
if(!lock)
return FALSE
- if(istype(I, /obj/item/key))
- if(lock.toggle(I))
- to_chat(user, SPAN_NOTICE("You [lock.status ? "lock" : "unlock"] \the [src] with \the [I]."))
+ if(!used_item.user_can_wield(user))
+ return FALSE
+ if(istype(used_item, /obj/item/key))
+ if(lock.toggle(used_item))
+ to_chat(user, SPAN_NOTICE("You [lock.status ? "lock" : "unlock"] \the [src] with \the [used_item]."))
else
- to_chat(user, SPAN_WARNING("\The [I] does not fit in the lock!"))
+ to_chat(user, SPAN_WARNING("\The [used_item] does not fit in the lock!"))
return TRUE
- if(istype(I, /obj/item/keyring))
- for(var/obj/item/key/key in I)
+ if(istype(used_item, /obj/item/keyring))
+ for(var/obj/item/key/key in used_item)
if(lock.toggle(key))
to_chat(user, SPAN_NOTICE("You [lock.status ? "lock" : "unlock"] \the [src] with \the [key]."))
return TRUE
- to_chat(user, SPAN_WARNING("\The [I] has no keys that fit in the lock!"))
+ to_chat(user, SPAN_WARNING("\The [used_item] has no keys that fit in the lock!"))
return TRUE
- if(lock.pick_lock(I,user))
+ if(lock.pick_lock(used_item,user))
return TRUE
if(lock.isLocked())
to_chat(user, SPAN_WARNING("\The [src] is locked!"))
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 3d5f43c2807..21e087a71d7 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -19,7 +19,10 @@
max_health = 100
var/damage_per_fire_tick = 2 // Amount of damage per fire tick. Regular windows are not fireproof so they might as well break quickly.
- var/construction_state = 2
+ var/const/CONSTRUCTION_STATE_NO_FRAME = 0
+ var/const/CONSTRUCTION_STATE_IN_FRAME = 1
+ var/const/CONSTRUCTION_STATE_FASTENED = 2
+ var/construction_state = CONSTRUCTION_STATE_FASTENED
var/id
var/polarized = 0
var/basestate = "window"
@@ -173,60 +176,90 @@
playsound(loc, 'sound/effects/Glasshit.ogg', 50, 1)
return TRUE
-/obj/structure/window/attackby(obj/item/W, mob/user)
- if(!istype(W)) return//I really wish I did not need this
-
- if(W.item_flags & ITEM_FLAG_NO_BLUDGEON) return
-
- if(IS_SCREWDRIVER(W))
- if(reinf_material && construction_state >= 1)
- construction_state = 3 - construction_state
+/obj/structure/window/handle_default_screwdriver_attackby(mob/user, obj/item/screwdriver)
+ var/tool_sound = screwdriver.get_tool_sound(TOOL_SCREWDRIVER) || 'sound/items/Screwdriver.ogg'
+ if(reinf_material) // reinforced windows have construction states
+ if(construction_state >= CONSTRUCTION_STATE_IN_FRAME) // if the window is in the frame
+ playsound(loc, tool_sound, 75, 1)
+ switch(construction_state)
+ if(CONSTRUCTION_STATE_IN_FRAME)
+ construction_state = CONSTRUCTION_STATE_FASTENED
+ to_chat(user, SPAN_NOTICE("You have fastened the window to the frame."))
+ if(CONSTRUCTION_STATE_FASTENED)
+ construction_state = CONSTRUCTION_STATE_IN_FRAME
+ to_chat(user, SPAN_NOTICE("You have unfastened the window from the frame."))
update_nearby_icons()
- playsound(loc, 'sound/items/Screwdriver.ogg', 75, 1)
- to_chat(user, (construction_state == 1 ? SPAN_NOTICE("You have unfastened the window from the frame.") : SPAN_NOTICE("You have fastened the window to the frame.")))
- else if(reinf_material && construction_state == 0)
- set_anchored(!anchored)
- playsound(loc, 'sound/items/Screwdriver.ogg', 75, 1)
+ return TRUE
+ else // if unanchored
+ set_anchored(!anchored) // sets construction_state for us
+ playsound(loc, tool_sound, 75, 1)
to_chat(user, (anchored ? SPAN_NOTICE("You have fastened the frame to the floor.") : SPAN_NOTICE("You have unfastened the frame from the floor.")))
- else if(!reinf_material)
- set_anchored(!anchored)
- playsound(loc, 'sound/items/Screwdriver.ogg', 75, 1)
- to_chat(user, (anchored ? SPAN_NOTICE("You have fastened the window to the floor.") : SPAN_NOTICE("You have unfastened the window.")))
- else if(IS_CROWBAR(W) && reinf_material && construction_state <= 1)
- construction_state = 1 - construction_state
- playsound(loc, 'sound/items/Crowbar.ogg', 75, 1)
- to_chat(user, (construction_state ? SPAN_NOTICE("You have pried the window into the frame.") : SPAN_NOTICE("You have pried the window out of the frame.")))
- else if(IS_WRENCH(W) && !anchored && (!construction_state || !reinf_material))
- if(!material)
- to_chat(user, SPAN_NOTICE("You're not sure how to dismantle \the [src] properly."))
- else
- playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)
- visible_message(SPAN_NOTICE("[user] dismantles \the [src]."))
- dismantle_structure(user)
- else if(IS_COIL(W) && is_fulltile())
- if (polarized)
- to_chat(user, SPAN_WARNING("\The [src] is already polarized."))
- return
- var/obj/item/stack/cable_coil/C = W
- if (C.use(1))
- playsound(src.loc, 'sound/effects/sparks1.ogg', 75, 1)
- polarized = TRUE
- to_chat(user, SPAN_NOTICE("You wire and polarize \the [src]."))
- else if (IS_WIRECUTTER(W))
- if (!polarized)
- to_chat(user, SPAN_WARNING("\The [src] is not polarized."))
- return
- new /obj/item/stack/cable_coil(get_turf(user), 1)
- if (opacity)
- toggle()
- polarized = FALSE
- id = null
- playsound(loc, 'sound/items/Wirecutter.ogg', 75, 1)
- to_chat(user, SPAN_NOTICE("You cut the wiring and remove the polarization from \the [src]."))
- else if(IS_MULTITOOL(W))
+ return TRUE
+ else // basic windows can only be anchored or unanchored
+ set_anchored(!anchored)
+ playsound(loc, tool_sound, 75, 1)
+ to_chat(user, (anchored ? SPAN_NOTICE("You have fastened the window to the floor.") : SPAN_NOTICE("You have unfastened the window.")))
+ return TRUE
+
+/obj/structure/window/handle_default_crowbar_attackby(mob/user, obj/item/crowbar)
+ if(!reinf_material || !anchored || construction_state > CONSTRUCTION_STATE_IN_FRAME)
+ return FALSE // ineligible, allow other interactions to proceed
+ switch(construction_state)
+ if(CONSTRUCTION_STATE_NO_FRAME) // pry the window into the frame
+ construction_state = CONSTRUCTION_STATE_IN_FRAME
+ to_chat(user, SPAN_NOTICE("You have pried the window into the frame."))
+ if(CONSTRUCTION_STATE_IN_FRAME)
+ construction_state = CONSTRUCTION_STATE_NO_FRAME
+ to_chat(user, SPAN_NOTICE("You have pried the window out of the frame."))
+ playsound(loc, crowbar.get_tool_sound(TOOL_CROWBAR) || 'sound/items/Crowbar.ogg', 75, 1)
+ return TRUE
+
+/obj/structure/window/handle_default_wrench_attackby(mob/user, obj/item/wrench)
+ if(anchored || (reinf_material && construction_state > CONSTRUCTION_STATE_NO_FRAME))
+ return FALSE // ineligible, allow other interactions to proceed
+ if(!material) // is this even necessary now? indestructible admin level window types maybe?
+ to_chat(user, SPAN_NOTICE("You're not sure how to dismantle \the [src] properly."))
+ return TRUE // prevent other interactions
+ playsound(loc, wrench.get_tool_sound(TOOL_WRENCH) || 'sound/items/Ratchet.ogg', 75, 1)
+ visible_message(SPAN_NOTICE("[user] dismantles \the [src]."))
+ dismantle_structure(user)
+ return TRUE
+
+/obj/structure/window/handle_default_cable_attackby(mob/user, obj/item/stack/cable_coil/coil)
+ if(!is_fulltile())
+ return FALSE // ineligible, allow other interactions to proceed
+ if(polarized)
+ to_chat(user, SPAN_WARNING("\The [src] is already polarized."))
+ return TRUE // prevent further interactions
+ if(coil.use(1))
+ playsound(loc, 'sound/effects/sparks1.ogg', 75, 1)
+ polarized = TRUE
+ to_chat(user, SPAN_NOTICE("You wire and polarize \the [src]."))
+ else
+ to_chat(user, SPAN_WARNING("You need at least one length of [coil.plural_name] to polarize \the [src]!"))
+ return TRUE
+
+/obj/structure/window/handle_default_wirecutter_attackby(mob/user, obj/item/wirecutters/wirecutters)
+ if (!polarized)
+ to_chat(user, SPAN_WARNING("\The [src] is not polarized."))
+ return TRUE // prevent other interactions
+ var/obj/item/stack/cable_coil/product = new /obj/item/stack/cable_coil(get_turf(user), 1)
+ if(product.add_to_stacks(user, TRUE))
+ user.put_in_hands(product)
+ if (opacity)
+ toggle() // must toggle off BEFORE unsetting the polarization var
+ polarized = FALSE
+ id = null
+ playsound(loc, wirecutters.get_tool_sound(TOOL_WIRECUTTERS) || 'sound/items/Wirecutter.ogg', 75, 1)
+ to_chat(user, SPAN_NOTICE("You cut the wiring and remove the polarization from \the [src]."))
+ return TRUE
+
+/obj/structure/window/attackby(obj/item/W, mob/user)
+ // bespoke interactions not handled by the prior procs
+ if(IS_MULTITOOL(W))
if (!polarized)
to_chat(user, SPAN_WARNING("\The [src] is not polarized."))
- return
+ return TRUE
if (anchored)
playsound(loc, 'sound/effects/pop.ogg', 75, 1)
to_chat(user, SPAN_NOTICE("You toggle \the [src]'s tinting."))
@@ -234,33 +267,42 @@
else
var/response = input(user, "New Window ID:", name, id) as null | text
if (isnull(response) || user.incapacitated() || !user.Adjacent(src) || user.get_active_held_item() != W)
- return
+ return TRUE
id = sanitize_safe(response, MAX_NAME_LEN)
to_chat(user, SPAN_NOTICE("The new ID of \the [src] is [id]."))
- return
+ return TRUE
else if(istype(W, /obj/item/gun/energy/plasmacutter) && anchored)
var/obj/item/gun/energy/plasmacutter/cutter = W
if(!cutter.slice(user))
- return
+ return TRUE // failed to finish or otherwise failed, prevent further interactions
playsound(src, 'sound/items/Welder.ogg', 80, 1)
visible_message(SPAN_NOTICE("[user] has started slicing through the window's frame!"))
- if(do_after(user,20,src))
+ if(do_after(user, 2 SECONDS, src))
visible_message(SPAN_WARNING("[user] has sliced through the window's frame!"))
playsound(src, 'sound/items/Welder.ogg', 80, 1)
- construction_state = 0
- set_anchored(0)
- else if (!istype(W, /obj/item/paint_sprayer))
- user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
- if(W.atom_damage_type == BRUTE || W.atom_damage_type == BURN)
- user.do_attack_animation(src)
- hit(W.force)
- if(current_health <= 7)
- set_anchored(FALSE)
- step(src, get_dir(user, src))
- else
- playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1)
- ..()
- return
+ set_anchored(FALSE)
+ if (istype(W, /obj/item/paint_sprayer))
+ return FALSE // allow afterattack to run
+ return ..() // handle generic interactions, bashing, etc
+
+/obj/structure/window/bash(obj/item/weapon, mob/user)
+ if(isliving(user) && user.a_intent == I_HELP)
+ return FALSE
+ if(!weapon.user_can_wield(user))
+ return FALSE
+ if(weapon.item_flags & ITEM_FLAG_NO_BLUDGEON)
+ return FALSE
+ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ // physical damage types that can impart force; swinging a bat or energy sword
+ if(weapon.atom_damage_type == BRUTE || weapon.atom_damage_type == BURN)
+ user.do_attack_animation(src)
+ hit(weapon.force)
+ if(current_health <= 7)
+ set_anchored(FALSE)
+ step(src, get_dir(user, src))
+ else
+ playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1)
+ return TRUE // bash successful
// TODO: generalize to matter list and parts_type.
/obj/structure/window/create_dismantled_products(turf/T)
@@ -302,6 +344,7 @@
return TRUE
/obj/structure/window/proc/hit(var/damage, var/sound_effect = 1)
+ // TODO: use reinf_material properties, paper reinforcement should be worse than plasteel reinforcement
if(reinf_material) damage *= 0.5
take_damage(damage)
@@ -352,11 +395,11 @@
to_chat(user, SPAN_NOTICE("It is reinforced with the [reinf_material.solid_name] lattice."))
if (reinf_material)
switch (construction_state)
- if (0)
+ if (CONSTRUCTION_STATE_NO_FRAME)
to_chat(user, SPAN_WARNING("The window is not in the frame."))
- if (1)
+ if (CONSTRUCTION_STATE_IN_FRAME)
to_chat(user, SPAN_WARNING("The window is pried into the frame but not yet fastened."))
- if (2)
+ if (CONSTRUCTION_STATE_FASTENED)
to_chat(user, SPAN_NOTICE("The window is fastened to the frame."))
if (anchored)
to_chat(user, SPAN_NOTICE("It is fastened to \the [get_turf(src)]."))
@@ -441,7 +484,7 @@
icon_state = "window_full"
/obj/structure/window/basic/full/polarized
- polarized = 1
+ polarized = TRUE
/obj/structure/window/borosilicate
name = "borosilicate window"
@@ -494,7 +537,7 @@
name = "electrochromic window"
desc = "Adjusts its tint with voltage. Might take a few good hits to shatter it."
basestate = "rwindow"
- polarized = 1
+ polarized = TRUE
/obj/structure/window/reinforced/polarized/full
dir = NORTHEAST
@@ -600,8 +643,7 @@
if (ST.use(required_amount))
var/obj/structure/window/WD = new(loc, ST.material.type, ST.reinf_material?.type, dir_to_set, FALSE)
to_chat(user, SPAN_NOTICE("You place [WD]."))
- WD.construction_state = 0
- WD.set_anchored(FALSE)
+ WD.set_anchored(FALSE) // handles setting construction state for us
else
to_chat(user, SPAN_NOTICE("You do not have enough sheets."))
return
diff --git a/code/game/turfs/floors/natural/natural_grass.dm b/code/game/turfs/floors/natural/natural_grass.dm
index 420700a8d87..78ae1bb8c8b 100644
--- a/code/game/turfs/floors/natural/natural_grass.dm
+++ b/code/game/turfs/floors/natural/natural_grass.dm
@@ -56,28 +56,22 @@
if(grass_color)
color = grass_color
- //#TODO: Check if this is still relevant/wanted since we got map gen to handle this?
- var/datum/extension/buried_resources/resources = get_or_create_extension(src, /datum/extension/buried_resources)
- if(prob(70))
- LAZYSET(resources.resources, /decl/material/solid/graphite, rand(3,5))
- if(prob(5))
- LAZYSET(resources.resources, /decl/material/solid/metal/uranium, rand(1,3))
- if(prob(2))
- LAZYSET(resources.resources, /decl/material/solid/gemstone/diamond, 1)
- if(!LAZYLEN(resources.resources))
- remove_extension(src, /datum/extension/buried_resources)
-
/turf/floor/natural/grass/wild/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
- if((temperature > T0C + 200 && prob(5)) || temperature > T0C + 1000)
+ // smoothly scale between 1/5 chance to scorch at ignition_point and 100% chance to scorch at ignition_point * 4
+ if(temperature >= material.ignition_point && prob(20 + temperature * 80 / (material.ignition_point * 4)))
handle_melting()
return ..()
/turf/floor/natural/grass/wild/handle_melting(list/meltable_materials)
. = ..()
- if(icon_state != "scorched")
- SetName("scorched ground")
- desc = "What was once lush grass has been reduced to burnt ashes."
- icon_state = "scorched"
- icon_edge_layer = -1
- footstep_type = /decl/footsteps/asteroid
- color = null
+ ChangeTurf(/turf/floor/natural/scorched)
+
+/turf/floor/natural/scorched
+ name = "scorched ground"
+ desc = "What was once lush grass has been reduced to burnt ashes."
+ icon = 'icons/turf/flooring/wildgrass.dmi'
+ icon_state = "scorched"
+ possible_states = null
+ footstep_type = /decl/footsteps/asteroid // closest we have to "stepping on carbonized grass"
+ material = /decl/material/solid/carbon
+ turf_flags = TURF_FLAG_BACKGROUND
diff --git a/code/game/turfs/walls/_wall.dm b/code/game/turfs/walls/_wall.dm
index 768b15f8288..d5e152c4372 100644
--- a/code/game/turfs/walls/_wall.dm
+++ b/code/game/turfs/walls/_wall.dm
@@ -44,7 +44,9 @@ var/global/list/wall_fullblend_objects = list(
var/decl/material/girder_material = /decl/material/solid/metal/steel
var/construction_stage
var/hitsound = 'sound/weapons/Genhit.ogg'
+ /// A list of connections to walls for each corner, used for icon generation. Can be converted to a list of dirs with corner_states_to_dirs().
var/list/wall_connections
+ /// A list of connections to non-walls for each corner, used for icon generation. Can be converted to a list of dirs with corner_states_to_dirs().
var/list/other_connections
var/floor_type = /turf/floor/plating //turf it leaves after destruction
var/paint_color
@@ -53,6 +55,15 @@ var/global/list/wall_fullblend_objects = list(
var/min_dismantle_amount = 2
var/max_dismantle_amount = 2
+ /// Icon to use if shutter state is non-null.
+ var/shutter_icon = 'icons/turf/walls/shutter.dmi'
+ /// TRUE = open, FALSE = closed, null = no shutter.
+ var/shutter_state
+ /// Overrides base material for shutter icon if set.
+ var/decl/material/shutter_material
+ /// Shutter open/close sound.
+ var/shutter_sound = 'sound/weapons/Genhit.ogg'
+
/turf/wall/Initialize(var/ml, var/materialtype, var/rmaterialtype)
..(ml)
@@ -62,6 +73,9 @@ var/global/list/wall_fullblend_objects = list(
icon_state = "blank"
color = null
+ if(ispath(shutter_material))
+ shutter_material = GET_DECL(shutter_material)
+
set_turf_materials((materialtype || material || get_default_material()), (rmaterialtype || reinf_material), TRUE, girder_material, skip_update = TRUE)
. = INITIALIZE_HINT_LATELOAD
diff --git a/code/game/turfs/walls/wall_attacks.dm b/code/game/turfs/walls/wall_attacks.dm
index 06c7269a780..0f152b0d3de 100644
--- a/code/game/turfs/walls/wall_attacks.dm
+++ b/code/game/turfs/walls/wall_attacks.dm
@@ -5,36 +5,22 @@
return
SSradiation.resistance_cache.Remove(src)
-
+ can_open = WALL_OPENING
+ sleep(15)
if(density)
- can_open = WALL_OPENING
- sleep(15)
- set_density(0)
- set_opacity(0)
+ set_density(FALSE)
blocks_air = ZONE_BLOCKED
- update_icon()
- update_air()
- set_light(0)
- src.blocks_air = 0
- set_opacity(0)
- for(var/turf/turf in loc)
- if(turf.simulated)
- SSair.mark_for_update(turf)
else
- can_open = WALL_OPENING
- set_density(1)
- set_opacity(1)
+ set_density(TRUE)
blocks_air = AIR_BLOCKED
- update_icon()
- update_air()
- sleep(15)
- set_light(1)
- src.blocks_air = 1
- set_opacity(1)
- for(var/turf/turf in loc)
- if(turf.simulated)
- SSair.mark_for_update(turf)
+ for(var/turf/turf in loc)
+ if(turf.simulated)
+ SSair.mark_for_update(turf)
+ set_light(density)
+ update_icon()
+ update_air()
+ refresh_opacity()
can_open = WALL_CAN_OPEN
update_icon()
@@ -62,22 +48,37 @@
else
to_chat(user, "\The [material.solid_name] [material.rotting_touch_message]!")
dismantle_turf()
- return
+ return TRUE
- if(!can_open)
+ if(can_open)
+ toggle_open(user)
+ return TRUE
+
+ if(!isnull(shutter_state))
+ shutter_state = !shutter_state
+ refresh_opacity()
+ blocks_air = shutter_state ? ZONE_BLOCKED : AIR_BLOCKED
+ if(simulated)
+ SSair.mark_for_update(src)
+ visible_message(SPAN_NOTICE("\The [user] [shutter_state ? "opens" : "closes"] the shutter."))
+ update_icon()
+ if(shutter_sound)
+ playsound(src, shutter_sound, 25, 1)
+ return TRUE
+
+ if (isnull(construction_stage) || !reinf_material)
to_chat(user, "You push \the [src], but nothing happens.")
playsound(src, hitsound, 25, 1)
- else if (isnull(construction_stage) || !reinf_material)
- toggle_open(user)
+ return TRUE
/turf/wall/attack_hand(var/mob/user)
radiate()
add_fingerprint(user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
- if(ishuman(user))
- var/mob/living/human/H = user
- var/obj/item/hand = GET_EXTERNAL_ORGAN(H, H.get_active_held_item_slot())
- if(hand && try_graffiti(H, hand))
+ if(isliving(user))
+ var/mob/living/user_living = user
+ var/obj/item/hand = GET_EXTERNAL_ORGAN(user_living, user_living.get_active_held_item_slot())
+ if(hand && try_graffiti(user_living, hand))
return TRUE
. = ..()
if(!.)
@@ -307,7 +308,7 @@
/turf/wall/attackby(var/obj/item/W, var/mob/user, click_params)
- if(istype(W, /obj/item/stack/tile/roof) || !user.check_dexterity(DEXTERITY_SIMPLE_MACHINES))
+ if(istype(W, /obj/item/stack/tile/roof) || !user.check_dexterity(DEXTERITY_SIMPLE_MACHINES) || !W.user_can_wield(user))
return ..()
if(handle_wall_tool_interactions(W, user))
diff --git a/code/game/turfs/walls/wall_brick.dm b/code/game/turfs/walls/wall_brick.dm
index dd4f244bce2..a8535f6472b 100644
--- a/code/game/turfs/walls/wall_brick.dm
+++ b/code/game/turfs/walls/wall_brick.dm
@@ -6,6 +6,10 @@
min_dismantle_amount = 3
max_dismantle_amount = 5
+/turf/wall/brick/shutter
+ shutter_state = FALSE
+ icon_state = "brick_shutter"
+
/turf/wall/brick/get_dismantle_stack_type()
return /obj/item/stack/material/brick
@@ -28,6 +32,10 @@
/turf/wall/brick/##material_name { \
color = /decl/material/solid/stone/##material_name::color; \
material = /decl/material/solid/stone/##material_name; \
+}; \
+/turf/wall/brick/##material_name/shutter { \
+ shutter_state = FALSE; \
+ icon_state = "brick_shutter"; \
}
MATERIAL_BRICK_WALL(sandstone)
diff --git a/code/game/turfs/walls/wall_icon.dm b/code/game/turfs/walls/wall_icon.dm
index a877a6926b9..d73c14e859c 100644
--- a/code/game/turfs/walls/wall_icon.dm
+++ b/code/game/turfs/walls/wall_icon.dm
@@ -1,3 +1,6 @@
+/turf/wall/proc/refresh_opacity()
+ return set_opacity(!(!density || shutter_state == TRUE || (istype(material) && material.opacity < 0.5)))
+
/turf/wall/proc/update_material(var/update_neighbors)
if(construction_stage != -1)
if(reinf_material)
@@ -12,7 +15,7 @@
if(reinf_material && reinf_material.explosion_resistance > explosion_resistance)
explosion_resistance = reinf_material.explosion_resistance
update_strings()
- set_opacity(material.opacity >= 0.5)
+ refresh_opacity()
SSradiation.resistance_cache.Remove(src)
if(update_neighbors)
var/iterate_turfs = list()
@@ -135,6 +138,12 @@
I = image(_get_wall_subicon(reinf_material.icon_reinf, wall_connections, reinf_color))
add_overlay(I)
+// Update icon on ambient light change, for shutter overlays.
+/turf/wall/update_ambient_light_from_z_or_area()
+ . = ..()
+ if(shutter_state)
+ queue_icon_update()
+
/turf/wall/on_update_icon()
. = ..()
cut_overlays()
@@ -149,6 +158,34 @@
if(texture)
add_overlay(texture)
+ if(!isnull(shutter_state) && shutter_icon)
+ var/decl/material/shutter_mat = shutter_material || material
+ var/list/shutters
+ var/list/connected = corner_states_to_dirs(wall_connections) | corner_states_to_dirs(other_connections) // merge the lists
+ for(var/stepdir in global.cardinal)
+ if(stepdir in connected)
+ continue
+ var/turf/neighbor = get_step_resolving_mimic(src, stepdir)
+ if(!istype(neighbor) || neighbor.density)
+ continue
+ LAZYADD(shutters, image(shutter_icon, num2text(shutter_state), dir = stepdir))
+ if(shutter_state)
+ var/turf/other_neighbor = get_step_resolving_mimic(src, global.reverse_dir[stepdir])
+ if(istype(other_neighbor))
+ var/light_amt = 255 * other_neighbor.get_lumcount()
+ if(light_amt > 0)
+ var/image/light_overlay = emissive_overlay(shutter_icon, "glow", dir = stepdir, color = other_neighbor.get_avg_color())
+ light_overlay.alpha = light_amt
+ light_overlay.appearance_flags |= RESET_COLOR|RESET_ALPHA
+ LAZYADD(shutters, light_overlay)
+
+ if(length(shutters))
+ var/image/shutter_image = new /image
+ shutter_image.overlays = shutters
+ shutter_image.color = shutter_mat.color
+ shutter_image.appearance_flags |= RESET_COLOR|RESET_ALPHA
+ add_overlay(shutter_image)
+
if(damage != 0 && SSmaterials.wall_damage_overlays)
var/integrity = material.integrity
if(reinf_material)
diff --git a/code/game/turfs/walls/wall_log.dm b/code/game/turfs/walls/wall_log.dm
index acca616ab88..90ca4b057e3 100644
--- a/code/game/turfs/walls/wall_log.dm
+++ b/code/game/turfs/walls/wall_log.dm
@@ -7,6 +7,10 @@
min_dismantle_amount = 3
max_dismantle_amount = 5
+/turf/wall/log/shutter
+ shutter_state = FALSE
+ icon_state = "log_shutter"
+
/turf/wall/log/get_dismantle_stack_type()
return /obj/item/stack/material/log
@@ -29,6 +33,10 @@
/turf/wall/log/##material_name { \
material = /decl/material/solid/organic/wood/##material_name; \
color = /decl/material/solid/organic/wood/##material_name::color; \
+}; \
+/turf/wall/log/##material_name/shutter { \
+ shutter_state = FALSE; \
+ icon_state = "log_shutter"; \
}
LOG_WALL_SUBTYPE(fungal)
diff --git a/code/game/turfs/walls/wall_natural_ramps.dm b/code/game/turfs/walls/wall_natural_ramps.dm
index 9a7eebf11a6..14c7a779e59 100644
--- a/code/game/turfs/walls/wall_natural_ramps.dm
+++ b/code/game/turfs/walls/wall_natural_ramps.dm
@@ -27,7 +27,7 @@
blocks_air = initial(blocks_air)
density = initial(density)
color = initial(color)
- set_opacity(!material || material.opacity >= 0.5)
+ refresh_opacity()
icon = 'icons/turf/walls/natural.dmi'
icon_state = "blank"
diff --git a/code/game/turfs/walls/wall_natural_subtypes.dm b/code/game/turfs/walls/wall_natural_subtypes.dm
index 279d40d10fb..ee192c9fc23 100644
--- a/code/game/turfs/walls/wall_natural_subtypes.dm
+++ b/code/game/turfs/walls/wall_natural_subtypes.dm
@@ -48,15 +48,19 @@
/turf/wall/natural/dirt
material = /decl/material/solid/soil
color = "#41311b"
+ floor_type = /turf/floor/natural/dirt
/turf/wall/natural/basalt
material = /decl/material/solid/stone/basalt
color = COLOR_DARK_GRAY
+ floor_type = /turf/floor/natural/rock/basalt
/turf/wall/natural/random/basalt
material = /decl/material/solid/stone/basalt
color = COLOR_DARK_GRAY
+ floor_type = /turf/floor/natural/rock/basalt
/turf/wall/natural/random/high_chance/basalt
material = /decl/material/solid/stone/basalt
color = COLOR_DARK_GRAY
+ floor_type = /turf/floor/natural/rock/basalt
diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm
index 09edd87fc8c..2af147ea958 100644
--- a/code/modules/assembly/assembly.dm
+++ b/code/modules/assembly/assembly.dm
@@ -114,17 +114,19 @@
return 0
-/obj/item/assembly/attackby(obj/item/W, mob/user)
- if(isassembly(W))
- var/obj/item/assembly/A = W
- if((!A.secured) && (!secured))
- attach_assembly(A,user)
+/obj/item/assembly/attackby(obj/item/component, mob/user)
+ if(!user_can_wield(user) || !component.user_can_wield(user))
+ return TRUE
+ if(isassembly(component))
+ var/obj/item/assembly/assembly = component
+ if(!assembly.secured && !secured)
+ attach_assembly(assembly, user)
return
- if(IS_SCREWDRIVER(W))
+ if(IS_SCREWDRIVER(component))
if(toggle_secure())
- to_chat(user, "\The [src] is ready!")
+ to_chat(user, SPAN_NOTICE("\The [src] is ready!"))
else
- to_chat(user, "\The [src] can now be attached!")
+ to_chat(user, SPAN_NOTICE("\The [src] can now be attached!"))
return
..()
return
@@ -144,10 +146,13 @@
/obj/item/assembly/attack_self(mob/user)
- if(!user) return 0
+ if(!user) // is this check even necessary outside of admin proccalls?
+ return FALSE
+ if(!user_can_wield(user))
+ return TRUE
user.set_machine(src)
interact(user)
- return 1
+ return TRUE
/obj/item/assembly/interact(mob/user)
return //HTML MENU FOR WIRES GOES HERE
diff --git a/code/modules/butchery/_butchery.dm b/code/modules/butchery/_butchery.dm
index aa166cb46af..972aa673cde 100644
--- a/code/modules/butchery/_butchery.dm
+++ b/code/modules/butchery/_butchery.dm
@@ -9,6 +9,9 @@
if(!IS_KNIFE(tool) || !butchery_data || stat != DEAD)
return FALSE
+ if(!tool.user_can_wield(user))
+ return TRUE // skip other interactions
+
var/decl/butchery_data/butchery_decl = GET_DECL(butchery_data)
if(!istype(butchery_decl))
return FALSE
diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm
index 514c94c0c3b..4f05723b4e1 100644
--- a/code/modules/clothing/chameleon.dm
+++ b/code/modules/clothing/chameleon.dm
@@ -163,7 +163,7 @@
/obj/item/clothing/suit/chameleon
name = "armor"
icon = 'icons/clothing/suits/armor/vest.dmi'
- desc = "It appears to be a vest of standard armor, except this is embedded with a hidden holographic cloaker, allowing it to change it's appearance, but offering no protection.. It seems to have a small dial inside."
+ desc = "It appears to be a vest of standard armor, except this is embedded with a hidden holographic cloaker, allowing it to change its appearance, but offering no protection. It seems to have a small dial inside."
origin_tech = @'{"esoteric":3}'
item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON
var/static/list/clothing_choices
diff --git a/code/modules/codex/entries/guides.dm b/code/modules/codex/entries/guides.dm
index a0a90e94640..4ed96a6c0cf 100644
--- a/code/modules/codex/entries/guides.dm
+++ b/code/modules/codex/entries/guides.dm
@@ -379,8 +379,8 @@
Unary vent: The basic vent used in rooms. It pumps gas into the room, but can't suck it back out. Controlled by the room's air alarm system.
Scrubber: The other half of room equipment. Filters air, and can suck it in entirely in what's called a "panic siphon." Activating a panic siphon without very good reason will kill someone. Don't do it.
Meter: A little box with some gauges and numbers. Fasten it to any pipe or manifold and it'll read you the pressure in it. Very useful.
- Gas mixer: Two sides are input, one side is output. Mixes the gases pumped into it at the ratio defined. The side perpendicular to the other two is "node 2," for reference, on non-mirrored mixers..
- Output is controlled by flow rate. There is also an "omni" variant that allows you to set input and output sections freely..
+ Gas mixer: Two sides are input, one side is output. Mixes the gases pumped into it at the ratio defined. The side perpendicular to the other two is "node 2," for reference, on non-mirrored mixers.
+ Output is controlled by flow rate. There is also an "omni" variant that allows you to set input and output sections freely.
Gas filter: Essentially the opposite of a gas mixer. One side is input. The other two sides are output. One gas type will be filtered into the perpendicular output pipe,
the rest will continue out the other side. Can also output from 0-4500 kPa. The "omni" vairant allows you to set input and output sections freely.
diff --git a/code/modules/crafting/stack_recipes/recipes_bricks.dm b/code/modules/crafting/stack_recipes/recipes_bricks.dm
index 58b49840c8b..0d42d0024fc 100644
--- a/code/modules/crafting/stack_recipes/recipes_bricks.dm
+++ b/code/modules/crafting/stack_recipes/recipes_bricks.dm
@@ -52,6 +52,10 @@
craft_stack_types = /obj/item/stack/material/brick
difficulty = MAT_VALUE_HARD_DIY
+/decl/stack_recipe/turfs/wall/brick/shutter
+ name = "shuttered brick wall"
+ result_type = /turf/wall/brick/shutter
+
/decl/stack_recipe/turfs/floor/brick
name = "cobblestone path"
result_type = /turf/floor/natural/path
diff --git a/code/modules/crafting/stack_recipes/recipes_logs.dm b/code/modules/crafting/stack_recipes/recipes_logs.dm
index 8b6a961f96a..6b2e9dff879 100644
--- a/code/modules/crafting/stack_recipes/recipes_logs.dm
+++ b/code/modules/crafting/stack_recipes/recipes_logs.dm
@@ -2,12 +2,18 @@
abstract_type = /decl/stack_recipe/logs
craft_stack_types = /obj/item/stack/material/log
forbidden_craft_stack_types = /obj/item/stack/material/ore
+
/decl/stack_recipe/turfs/wall/logs
name = "log wall"
result_type = /turf/wall/log
craft_stack_types = /obj/item/stack/material/log
forbidden_craft_stack_types = /obj/item/stack/material/ore
difficulty = MAT_VALUE_HARD_DIY
+
+/decl/stack_recipe/turfs/wall/logs/shutter
+ name = "shuttered log wall"
+ result_type = /turf/wall/log/shutter
+
/decl/stack_recipe/logs/wall_frame
result_type = /obj/structure/wall_frame/log
difficulty = MAT_VALUE_HARD_DIY
\ No newline at end of file
diff --git a/code/modules/emotes/definitions/audible.dm b/code/modules/emotes/definitions/audible.dm
index b2645606434..dfdfb95aa87 100644
--- a/code/modules/emotes/definitions/audible.dm
+++ b/code/modules/emotes/definitions/audible.dm
@@ -33,7 +33,7 @@
/decl/emote/audible/gnarl
key = "gnarl"
- emote_message_3p = "$USER$ gnarls and shows its teeth.."
+ emote_message_3p = "$USER$ gnarls and shows its teeth."
/decl/emote/audible/multichirp
key = "mchirp"
diff --git a/code/modules/events/meteors.dm b/code/modules/events/meteors.dm
index 52be544306c..824d31b395a 100644
--- a/code/modules/events/meteors.dm
+++ b/code/modules/events/meteors.dm
@@ -112,9 +112,9 @@ var/global/list/meteors_major = list(
/datum/event/meteor_wave/overmap/tick()
if(!victim)
return
- if (victim.is_still() || victim.get_helm_skill() >= SKILL_ADEPT) //Unless you're standing or good at your job..
+ if (victim.is_still() || victim.get_helm_skill() >= SKILL_ADEPT) //Unless you're standing still or good at your job...
start_side = pick(global.cardinal)
- else //..Meteors mostly fly in your face
+ else //... Meteors mostly fly in your face
start_side = prob(90) ? victim.fore_dir : pick(global.cardinal)
..()
@@ -242,9 +242,6 @@ var/global/list/meteors_major = list(
/obj/effect/meteor/Initialize()
. = ..()
z_original = z
-
-/obj/effect/meteor/Initialize()
- . = ..()
global.meteor_list += src
/obj/effect/meteor/Move()
@@ -258,7 +255,7 @@ var/global/list/meteors_major = list(
qdel(src)
/obj/effect/meteor/Destroy()
- walk(src,0) //this cancels the walk_towards() proc
+ walk(src, 0)
global.meteor_list -= src
. = ..()
diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm
index d92bed53478..14dcdbae837 100644
--- a/code/modules/holodeck/HolodeckObjects.dm
+++ b/code/modules/holodeck/HolodeckObjects.dm
@@ -144,27 +144,15 @@
name = "boxing gloves"
desc = "Because you really needed another excuse to punch your crewmates."
-/obj/structure/window/holowindow/full
+/obj/structure/window/reinforced/holowindow/full
dir = NORTHEAST
- icon_state = "window_full"
+ icon_state = "rwindow_full"
-/obj/structure/window/reinforced/holowindow/attackby(obj/item/W, mob/user)
-
- if(!istype(W) || W.item_flags & ITEM_FLAG_NO_BLUDGEON) return
-
- if(IS_SCREWDRIVER(W) || IS_CROWBAR(W) || IS_WRENCH(W))
- to_chat(user, ("It's a holowindow, you can't dismantle it!"))
- else
- if(W.atom_damage_type == BRUTE || W.atom_damage_type == BURN)
- hit(W.force)
- if(current_health <= 7)
- anchored = FALSE
- update_nearby_icons()
- step(src, get_dir(user, src))
- else
- playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1)
- ..()
- return
+/obj/structure/window/reinforced/holowindow/attackby(obj/item/weapon, mob/user)
+ if(IS_SCREWDRIVER(weapon) || IS_CROWBAR(weapon) || IS_WRENCH(weapon))
+ to_chat(user, SPAN_NOTICE("It's a holowindow, you can't dismantle it!"))
+ return TRUE
+ return bash(weapon, user)
/obj/structure/window/reinforced/holowindow/shatter(var/display_message = 1)
playsound(src, "shatter", 70, 1)
@@ -173,6 +161,7 @@
qdel(src)
return
+// This subtype is deleted when a ready button in the same area is pressed.
/obj/structure/window/reinforced/holowindow/disappearing
/obj/machinery/door/window/holowindoor/attackby(obj/item/I, mob/user)
@@ -199,14 +188,15 @@
close()
else if (src.density)
- flick(text("[]deny", src.base_state), src)
+ flick("[base_state]deny", src)
-/obj/machinery/door/window/holowindoor/shatter(var/display_message = 1)
- src.set_density(0)
- playsound(src, "shatter", 70, 1)
+/obj/machinery/door/window/holowindoor/shatter(var/display_message = TRUE)
+ set_density(FALSE)
+ playsound(loc, "shatter", 70, TRUE)
if(display_message)
visible_message("[src] fades away as it shatters!")
- qdel(src)
+ animate(src, 0.5 SECONDS, alpha = 0)
+ QDEL_IN_CLIENT_TIME(src, 0.5 SECONDS)
/obj/structure/bed/holobed
tool_interaction_flags = 0
diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm
index 36e96954952..9d0754c45dd 100644
--- a/code/modules/hydroponics/grown.dm
+++ b/code/modules/hydroponics/grown.dm
@@ -306,24 +306,22 @@ var/global/list/_wood_materials = list(
/obj/item/chems/food/grown/attack_self(mob/user)
- if(!seed)
- return
-
- if(isspaceturf(user.loc))
- return
+ if(seed)
+ if(user.a_intent == I_HURT)
+ user.visible_message(SPAN_DANGER("\The [user] squashes \the [src]!"))
+ seed.thrown_at(src,user)
+ sleep(-1)
+ if(src) qdel(src)
+ return TRUE
- if(user.a_intent == I_HURT)
- user.visible_message("\The [user] squashes \the [src]!")
- seed.thrown_at(src,user)
- sleep(-1)
- if(src) qdel(src)
- return
+ var/turf/user_loc = user.loc
+ if(isturf(user_loc) && !user_loc.is_open() && seed.get_trait(TRAIT_SPREAD) > 0)
+ to_chat(user, SPAN_NOTICE("You plant \the [src]."))
+ new /obj/machinery/portable_atmospherics/hydroponics/soil/invisible(get_turf(user),src.seed)
+ qdel(src)
+ return TRUE
- if(seed.get_trait(TRAIT_SPREAD) > 0)
- to_chat(user, "You plant the [src.name].")
- new /obj/machinery/portable_atmospherics/hydroponics/soil/invisible(get_turf(user),src.seed)
- qdel(src)
- return
+ return ..()
/obj/item/chems/food/grown/on_picked_up(mob/user)
..()
diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm
index 7d6b5e8f50c..e55388d3e86 100644
--- a/code/modules/integrated_electronics/core/assemblies.dm
+++ b/code/modules/integrated_electronics/core/assemblies.dm
@@ -72,7 +72,7 @@
visible_message(SPAN_DANGER("\The [src] starts to break apart!"))
/obj/item/electronic_assembly/proc/check_interactivity(mob/user)
- return (!user.incapacitated() && CanUseTopic(user))
+ return (!user.incapacitated() && CanUseTopic(user) && user_can_wield(user))
/obj/item/electronic_assembly/GetAccess()
. = list()
diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm
index aeff7927052..dff167e4919 100644
--- a/code/modules/integrated_electronics/subtypes/input.dm
+++ b/code/modules/integrated_electronics/subtypes/input.dm
@@ -195,7 +195,7 @@
var/mob/living/human/H = get_pin_data_as_type(IC_INPUT, 1, /mob/living)
if(!istype(H)) //Invalid input
return
- if(H in view(get_turf(src))) // Like medbot's analyzer it can be used in range..
+ if(H in view(get_turf(src))) // Like the medbot's analyzer it can be used at range.
var/current_max_health = H.get_max_health()
var/obj/item/organ/internal/brain = GET_INTERNAL_ORGAN(H, BP_BRAIN)
@@ -254,7 +254,7 @@
return
for(var/i=1, i<=outputs.len, i++)
set_pin_data(IC_OUTPUT, i, null)
- if(H in view(get_turf(src))) // Like medbot's analyzer it can be used in range..
+ if(H in view(get_turf(src))) // Like the medbot's analyzer it can be used at range.
if(H.seed)
set_pin_data(IC_OUTPUT, 1, H.seed.product_name)
set_pin_data(IC_OUTPUT, 2, H.age)
@@ -297,7 +297,7 @@
return
for(var/i=1, i<=outputs.len, i++)
set_pin_data(IC_OUTPUT, i, null)
- if(H in view(get_turf(src))) // Like medbot's analyzer it can be used in range..
+ if(H in view(get_turf(src))) // Like the medbot's analyzer it can be used at range.
if(H.seed)
for(var/chem_path in H.seed.chems)
var/decl/material/R = chem_path
diff --git a/code/modules/item_effects/item_effect_charges.dm b/code/modules/item_effects/item_effect_charges.dm
index 939c1d7c63b..4a27d3d0a27 100644
--- a/code/modules/item_effects/item_effect_charges.dm
+++ b/code/modules/item_effects/item_effect_charges.dm
@@ -12,6 +12,52 @@
/decl/item_effect/charges/examined(obj/item/item, mob/user)
to_chat(user, SPAN_NOTICE("\The [item] has [item.get_item_effect_parameter(src, ITEM_EFFECT_RANGED, "charges") || 0] charge\s of [effect_descriptor] left."))
+/obj/item/projectile/fireball
+ name = "fireball"
+ icon_state = "fireball"
+ fire_sound = 'sound/effects/bamf.ogg'
+ damage = 20
+ atom_damage_type = BURN
+ damage_flags = DAM_DISPERSED // burn all over
+ var/fire_lifetime = 2 SECONDS
+ var/fire_temperature = (288 CELSIUS) / 0.9 + 1 // hot enough to ignite wood! divided by 0.9 and plus one to ensure we can light firepits
+
+/obj/effect/fake_fire/variable
+ name = "fire"
+ anchored = TRUE
+ mouse_opacity = MOUSE_OPACITY_UNCLICKABLE
+ firelevel = 1
+ pressure = ONE_ATMOSPHERE
+
+/obj/effect/fake_fire/variable/Initialize(ml, new_temperature, new_lifetime)
+ lifetime = new_lifetime
+ last_temperature = new_temperature
+ return ..()
+
+// we deal our damage via fire_act, not via direct burn damage. our burn damage is specifically for mobs
+/obj/item/projectile/fireball/get_structure_damage()
+ return 0
+
+/obj/item/projectile/fireball/on_impact(var/atom/A)
+ . = ..()
+ var/obj/effect/fake_fire/fire = new /obj/effect/fake_fire/variable(get_turf(A), fire_temperature, fire_lifetime)
+ fire.Process() // process at least once!
+ qdel_self()
+
+/obj/item/projectile/fireball/after_move()
+ . = ..()
+ if(!loc)
+ return
+ for(var/mob/living/victim in loc)
+ if(!victim.simulated)
+ continue
+ victim.FireBurn(1, fire_temperature, ONE_ATMOSPHERE)
+ loc.fire_act(1, fire_temperature, ONE_ATMOSPHERE)
+ for(var/atom/burned in loc)
+ if(!burned.simulated || burned == src)
+ continue
+ burned.fire_act(1, fire_temperature, ONE_ATMOSPHERE)
+
// Example effect; casts a fireball n times.
/decl/item_effect/charges/fireball
effect_descriptor = "fireball"
@@ -20,5 +66,5 @@
/decl/item_effect/charges/fireball/do_ranged_effect(mob/user, obj/item/item, atom/target, list/parameters)
. = ..()
if(.)
- var/obj/item/projectile/spell_projectile/fireball/projectile = new(get_turf(user))
+ var/obj/item/projectile/fireball/projectile = new(get_turf(user))
projectile.launch_from_gun(target, user.get_target_zone(), user)
diff --git a/code/modules/item_effects/item_effect_debug.dm b/code/modules/item_effects/item_effect_debug.dm
index a14237d437e..501110b7aee 100644
--- a/code/modules/item_effects/item_effect_debug.dm
+++ b/code/modules/item_effects/item_effect_debug.dm
@@ -62,3 +62,16 @@
ITEM_EFFECT_VISIBLE,
ITEM_EFFECT_WIELDED
))
+
+/obj/item/staff/fireball
+ name = "staff of fireball"
+ icon_state = "starstaff"
+ material = /decl/material/solid/organic/wood/ebony
+ matter = list(/decl/material/solid/metal/gold = MATTER_AMOUNT_TRACE)
+
+/obj/item/staff/fireball/Initialize(ml, material_key)
+ . = ..()
+ add_item_effect(/decl/item_effect/charges/fireball, list(
+ ITEM_EFFECT_VISIBLE,
+ ITEM_EFFECT_RANGED = list("charges" = 5)
+ ))
\ No newline at end of file
diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm
index 761ea49d37f..1004ad8c7f9 100644
--- a/code/modules/lighting/lighting_turf.dm
+++ b/code/modules/lighting/lighting_turf.dm
@@ -151,11 +151,11 @@
lum_g += L.apparent_g
lum_b += L.apparent_b
- lum_r = CLAMP01(lum_r / 4) * 255
- lum_g = CLAMP01(lum_g / 4) * 255
- lum_b = CLAMP01(lum_b / 4) * 255
+ lum_r = CLAMP01(lum_r / length(corners)) * 255
+ lum_g = CLAMP01(lum_g / length(corners)) * 255
+ lum_b = CLAMP01(lum_b / length(corners)) * 255
- return rgb(lum_r, lum_b, lum_g)
+ return rgb(lum_r, lum_g, lum_b)
#define SCALE(targ,min,max) (targ - min) / (max - min)
diff --git a/code/modules/maps/template_types/random_exoplanet/planet_themes/ruined_city.dm b/code/modules/maps/template_types/random_exoplanet/planet_themes/ruined_city.dm
index 465fe33d134..b8e70d839d6 100644
--- a/code/modules/maps/template_types/random_exoplanet/planet_themes/ruined_city.dm
+++ b/code/modules/maps/template_types/random_exoplanet/planet_themes/ruined_city.dm
@@ -25,7 +25,7 @@
var/datum/level_data/LD = SSmapping.levels_by_id[E.surface_level_id]
var/area/A = LD.get_base_area_instance()
if(istype(A, world.area))
- PRINT_STACK_TRACE("Got '[world.area]' area as area for the surface level '[LD]' of planetoid '[E]'.") //Don't modify the ambience of the space area..
+ PRINT_STACK_TRACE("Got '[world.area]' area as area for the surface level '[LD]' of planetoid '[E]'.") //Don't modify the ambience of the space area.
LAZYDISTINCTADD(A.ambience, spooky_ambience)
/datum/exoplanet_theme/ruined_city/get_sensor_data()
diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/barren.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/barren.dm
index 789050b5ed7..6e36216c345 100644
--- a/code/modules/maps/template_types/random_exoplanet/planet_types/barren.dm
+++ b/code/modules/maps/template_types/random_exoplanet/planet_types/barren.dm
@@ -36,7 +36,7 @@
// Planetoid Data
////////////////////////////////////////////////////////////////////////////
-//#FIXME: Barren is kind of a wide encompassing planet type. There's a lot of extremes to take into accounts for a single type..
+//#FIXME: Barren is kind of a wide encompassing planet type. There's a lot of extremes to take into accounts for a single type...
/datum/planetoid_data/random/barren
habitability_class = null
diff --git a/code/modules/mining/ore_box.dm b/code/modules/mining/ore_box.dm
index ccf7b91373b..c7ae5b9d18a 100644
--- a/code/modules/mining/ore_box.dm
+++ b/code/modules/mining/ore_box.dm
@@ -50,7 +50,7 @@
for(var/obj/item/stack/material/ore/O in ores)
if(total_ores >= maximum_ores)
if(user)
- to_chat(user, SPAN_WARNING("You insert only what you can.."))
+ to_chat(user, SPAN_WARNING("You insert only what you can."))
break
inserted += O.amount
insert_ore(O)
diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm
index b57a252b2ed..1b7d470abc8 100644
--- a/code/modules/mob/death.dm
+++ b/code/modules/mob/death.dm
@@ -76,7 +76,7 @@
if(stat == DEAD)
return FALSE
- walk(src, 0)
+ stop_automove()
facing_dir = null
if(!gibbed)
diff --git a/code/modules/mob/grab/grab_object.dm b/code/modules/mob/grab/grab_object.dm
index 1a598a635c0..4f008fbfe3b 100644
--- a/code/modules/mob/grab/grab_object.dm
+++ b/code/modules/mob/grab/grab_object.dm
@@ -7,6 +7,7 @@
drop_sound = null
equip_sound = null
is_spawnable_type = FALSE
+ needs_attack_dexterity = DEXTERITY_GRAPPLE
var/atom/movable/affecting // Atom being targeted by this grab.
var/mob/assailant // Mob that instantiated this grab.
diff --git a/code/modules/mob/living/bot/remotebot.dm b/code/modules/mob/living/bot/remotebot.dm
index ec98235438f..1d50e2f72c6 100644
--- a/code/modules/mob/living/bot/remotebot.dm
+++ b/code/modules/mob/living/bot/remotebot.dm
@@ -81,7 +81,7 @@
if(working || stat || !on || a == src) //can't touch itself
return
if(isturf(a) || get_dist(src,a) > 1)
- walk_to(src,a,0,get_movement_delay(get_dir(src, a)))
+ start_automove(a)
else if(istype(a, /obj/item))
pickup(a)
else
diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm
index 947f989f447..412d45498ac 100644
--- a/code/modules/mob/living/bot/secbot.dm
+++ b/code/modules/mob/living/bot/secbot.dm
@@ -150,7 +150,7 @@
..()
events_repository.unregister(/decl/observ/moved, target, src)
awaiting_surrender = -1
- walk_to(src, 0)
+ stop_automove()
/mob/living/bot/secbot/startPatrol()
if(!locked) // Stop running away when we set you up
diff --git a/code/modules/mob/living/human/human_defines.dm b/code/modules/mob/living/human/human_defines.dm
index 71c332ff74b..8b9c4db0325 100644
--- a/code/modules/mob/living/human/human_defines.dm
+++ b/code/modules/mob/living/human/human_defines.dm
@@ -1,6 +1,6 @@
/mob/living/human
- ai = /datum/ai/human
+ ai = /datum/mob_controller/human
mob_bump_flag = HUMAN
mob_push_flags = ~HEAVY
mob_swap_flags = ~HEAVY
diff --git a/code/modules/mob/living/human/life.dm b/code/modules/mob/living/human/life.dm
index 8bc960c9226..b084a03d85b 100644
--- a/code/modules/mob/living/human/life.dm
+++ b/code/modules/mob/living/human/life.dm
@@ -268,8 +268,7 @@
bodytemperature += round(robolimb_count/2)
return ..()
-//This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, SLOT_UPPER_BODY, SLOT_LOWER_BODY, etc. See setup.dm for the full list)
-/mob/living/human/proc/get_heat_protection_flags(temperature) //Temperature is the temperature you're being exposed to.
+/mob/living/human/get_heat_protection_flags(temperature)
. = 0
//Handle normal clothing
for(var/slot in global.standard_clothing_slots)
@@ -811,36 +810,6 @@
hud_list[SPECIALROLE_HUD] = holder
hud_updateflag = 0
-/mob/living/human/handle_fire()
- if(..())
- return
-
- var/burn_temperature = fire_burn_temperature()
- var/thermal_protection = get_heat_protection(burn_temperature)
-
- if (thermal_protection < 1 && bodytemperature < burn_temperature)
- bodytemperature += round(BODYTEMP_HEATING_MAX*(1-thermal_protection), 1)
-
- var/species_heat_mod = 1
-
- var/protected_limbs = get_heat_protection_flags(burn_temperature)
-
-
- if(species)
- if(burn_temperature < get_mob_temperature_threshold(HEAT_LEVEL_2))
- species_heat_mod = 0.5
- else if(burn_temperature < get_mob_temperature_threshold(HEAT_LEVEL_3))
- species_heat_mod = 0.75
-
- burn_temperature -= get_mob_temperature_threshold(HEAT_LEVEL_1)
-
- if(burn_temperature < 1)
- return
-
- for(var/obj/item/organ/external/E in get_external_organs())
- if(!(E.body_part & protected_limbs) && prob(20))
- E.take_external_damage(burn = round(species_heat_mod * log(10, (burn_temperature + 10)), 0.1), used_weapon = "fire")
-
/mob/living/human/rejuvenate()
reset_blood()
full_prosthetic = null
diff --git a/code/modules/mob/living/human/update_icons.dm b/code/modules/mob/living/human/update_icons.dm
index 0109853188c..f512877f72b 100644
--- a/code/modules/mob/living/human/update_icons.dm
+++ b/code/modules/mob/living/human/update_icons.dm
@@ -531,13 +531,6 @@ Please contact me on #coderbus IRC. ~Carn x
return
set_tail_state("[tail_organ.get_tail()]_static")
-/mob/living/human/update_fire(var/update_icons=1)
- if(on_fire)
- var/image/standing = overlay_image(get_bodytype()?.get_ignited_icon(src) || 'icons/mob/OnFire.dmi', "Standing", RESET_COLOR)
- set_current_mob_overlay(HO_FIRE_LAYER, standing, update_icons)
- else
- set_current_mob_overlay(HO_FIRE_LAYER, null, update_icons)
-
/mob/living/human/hud_reset(full_reset = FALSE)
if((. = ..()) && internals && internal)
internals.icon_state = "internal1"
diff --git a/code/modules/mob/living/living_bodytemp.dm b/code/modules/mob/living/living_bodytemp.dm
index eee7572b398..0d9d6c6c41b 100644
--- a/code/modules/mob/living/living_bodytemp.dm
+++ b/code/modules/mob/living/living_bodytemp.dm
@@ -41,3 +41,8 @@
if(get_hydration() >= hyd_remove)
adjust_hydration(-hyd_remove)
bodytemperature += min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM)
+
+/// This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, SLOT_UPPER_BODY, SLOT_LOWER_BODY, etc.
+/// Temperature parameter is the temperature you're being exposed to.
+/mob/living/proc/get_heat_protection_flags(temperature)
+ return 0
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index b74e516c2d4..7a400561ca7 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -246,7 +246,7 @@
if(!istype(wall) || !wall.density)
return FALSE
LAZYDISTINCTADD(pinned, O)
- walk_to(src, 0) // cancel any automated movement
+ stop_automove()
visible_message("\The [src] is pinned to \the [wall] by \the [O]!")
// TODO: cancel all throwing and momentum after this point
return TRUE
@@ -301,8 +301,13 @@
set_light(0)
update_fire()
-/mob/living/proc/update_fire()
- return
+/mob/living/proc/update_fire(var/update_icons=1)
+ if(on_fire)
+ var/decl/bodytype/mob_bodytype = get_bodytype()
+ var/image/standing = overlay_image(mob_bodytype?.get_ignited_icon(src) || 'icons/mob/OnFire.dmi', mob_bodytype?.get_ignited_icon_state(src) || "Generic_mob_burning", RESET_COLOR)
+ set_current_mob_overlay(HO_FIRE_LAYER, standing, update_icons)
+ else
+ set_current_mob_overlay(HO_FIRE_LAYER, null, update_icons)
/mob/living/proc/adjust_fire_stacks(add_fire_stacks) //Adjusting the amount of fire_stacks we have on person
fire_stacks = clamp(fire_stacks + add_fire_stacks, FIRE_MIN_STACKS, FIRE_MAX_STACKS)
@@ -312,21 +317,48 @@
fire_stacks = min(0, ++fire_stacks) //If we've doused ourselves in water to avoid fire, dry off slowly
if(!on_fire)
- return 1
+ return TRUE
else if(fire_stacks <= 0)
ExtinguishMob() //Fire's been put out.
- return 1
+ return TRUE
fire_stacks = max(0, fire_stacks - 0.2) //I guess the fire runs out of fuel eventually
var/datum/gas_mixture/G = loc.return_air() // Check if we're standing in an oxygenless environment
if(G.get_by_flag(XGM_GAS_OXIDIZER) < 1)
ExtinguishMob() //If there's no oxygen in the tile we're on, put out the fire
- return 1
+ return TRUE
var/turf/location = get_turf(src)
location.hotspot_expose(fire_burn_temperature(), 50, 1)
+ var/burn_temperature = fire_burn_temperature()
+ var/thermal_protection = get_heat_protection(burn_temperature)
+
+ if (thermal_protection < 1 && bodytemperature < burn_temperature)
+ bodytemperature += round(BODYTEMP_HEATING_MAX*(1-thermal_protection), 1)
+
+ var/species_heat_mod = 1
+
+ var/protected_limbs = get_heat_protection_flags(burn_temperature)
+
+ if(burn_temperature < get_mob_temperature_threshold(HEAT_LEVEL_2))
+ species_heat_mod = 0.5
+ else if(burn_temperature < get_mob_temperature_threshold(HEAT_LEVEL_3))
+ species_heat_mod = 0.75
+
+ burn_temperature -= get_mob_temperature_threshold(HEAT_LEVEL_1)
+
+ if(burn_temperature < 1)
+ return
+
+ if(has_external_organs())
+ for(var/obj/item/organ/external/E in get_external_organs())
+ if(!(E.body_part & protected_limbs) && prob(20))
+ E.take_external_damage(burn = round(species_heat_mod * log(10, (burn_temperature + 10)), 0.1), used_weapon = "fire")
+ else // fallback for simplemobs
+ take_damage(round(species_heat_mod * log(10, (burn_temperature + 10))), 0.1, BURN, DAM_DISPERSED)
+
/mob/living/proc/increase_fire_stacks(exposed_temperature)
if(fire_stacks <= 4 || fire_burn_temperature() < exposed_temperature)
adjust_fire_stacks(2)
diff --git a/code/modules/mob/living/living_throw.dm b/code/modules/mob/living/living_throw.dm
index 42ac87ba10b..550c76feab3 100644
--- a/code/modules/mob/living/living_throw.dm
+++ b/code/modules/mob/living/living_throw.dm
@@ -7,6 +7,8 @@
if(incapacitated() || !target || istype(target, /obj/screen) || !istype(item) || !(item in get_held_items()))
return FALSE
+ var/place_item = a_intent != I_HURT && Adjacent(target)
+
if(istype(item, /obj/item/grab))
var/obj/item/grab/G = item
item = G.throw_held()
@@ -21,14 +23,12 @@
var/end_T_descriptor = "[start_T] \[[end_T.x],[end_T.y],[end_T.z]\] ([end_T.loc])"
admin_attack_log(usr, mob, "Threw the victim from [start_T_descriptor] to [end_T_descriptor].", "Was from [start_T_descriptor] to [end_T_descriptor].", "threw, from [start_T_descriptor] to [end_T_descriptor], ")
drop_from_inventory(G)
- else if(!try_unequip(item, play_dropsound = FALSE))
+ else if(!try_unequip(item, play_dropsound = place_item && !isliving(target)))
return FALSE
// Hand items to a nearby target, or place them on the turf.
- if(a_intent != I_HURT && Adjacent(target) && isitem(item) && !QDELETED(item) && !QDELETED(target))
- if(!try_unequip(item))
- return FALSE
- item.dropInto(get_turf(target))
+ if(place_item && !QDELETED(item) && !QDELETED(target))
+ // We've already been unequipped above.
if(isliving(target))
var/mob/living/mob = target
if(length(mob.get_held_item_slots()))
@@ -42,6 +42,8 @@
to_chat(src, SPAN_NOTICE("You offer \the [item] to \the [mob]."))
do_give(mob)
return TRUE
+ to_chat(src, SPAN_WARNING("\The [mob] has no way to hold \the [item]!"))
+ return TRUE
if(!QDELETED(item))
item.forceMove(get_turf(target))
return TRUE
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index ff78cd41471..043435c127d 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -74,19 +74,19 @@
/mob/living/silicon/robot/drone/can_be_possessed_by(var/mob/observer/ghost/possessor)
if(!istype(possessor) || !possessor.client || !possessor.ckey)
- return 0
+ return FALSE
if(!get_config_value(/decl/config/toggle/on/allow_drone_spawn))
- to_chat(src, "Playing as drones is not currently permitted.")
- return 0
+ to_chat(possessor, SPAN_DANGER("Playing as drones is not currently permitted."))
+ return FALSE
if(too_many_active_drones())
- to_chat(src, "The maximum number of active drones has been reached..")
- return 0
+ to_chat(possessor, SPAN_DANGER("The maximum number of active drones has been reached."))
+ return FALSE
if(jobban_isbanned(possessor,ASSIGNMENT_ROBOT))
- to_chat(usr, "You are banned from playing synthetics and cannot spawn as a drone.")
- return 0
+ to_chat(possessor, SPAN_DANGER("You are banned from playing synthetics and cannot spawn as a drone."))
+ return FALSE
if(!possessor.MayRespawn(1,DRONE_SPAWN_DELAY))
- return 0
- return 1
+ return FALSE
+ return TRUE
/mob/living/silicon/robot/drone/do_possession(var/mob/observer/ghost/possessor)
if(!(istype(possessor) && possessor.ckey))
@@ -134,7 +134,7 @@
//Redefining some robot procs...
/mob/living/silicon/robot/drone/fully_replace_character_name(pickedName as text)
- // Would prefer to call the grandparent proc but this isn't possible, so..
+ // Would prefer to call the grandparent proc but this isn't possible, so...
real_name = pickedName
SetName(real_name)
diff --git a/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm b/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm
index aadbc20da26..b4792456a2c 100644
--- a/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm
+++ b/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm
@@ -1,7 +1,6 @@
/mob/living/simple_animal/aquatic
icon = 'icons/mob/simple_animal/fish_content.dmi'
- turns_per_move = 5
- speed = 4
+ turns_per_wander = 5
mob_size = MOB_SIZE_SMALL
emote_see = list("glubs", "blubs", "bloops")
base_animal_type = /mob/living/simple_animal/aquatic
diff --git a/code/modules/mob/living/simple_animal/aquatic/_aquatic_hostile.dm b/code/modules/mob/living/simple_animal/aquatic/_aquatic_hostile.dm
index b537fe3850d..dae75864a02 100644
--- a/code/modules/mob/living/simple_animal/aquatic/_aquatic_hostile.dm
+++ b/code/modules/mob/living/simple_animal/aquatic/_aquatic_hostile.dm
@@ -1,12 +1,11 @@
/mob/living/simple_animal/hostile/aquatic
abstract_type = /mob/living/simple_animal/hostile/aquatic
icon = 'icons/mob/simple_animal/fish_content.dmi'
- turns_per_move = 5
natural_weapon = /obj/item/natural_weapon/bite
- speed = 4
mob_size = MOB_SIZE_MEDIUM
emote_see = list("gnashes")
base_animal_type = /mob/living/simple_animal/aquatic // used for language, ignore actual type
is_aquatic = TRUE
butchery_data = /decl/butchery_data/animal/fish
holder_type = /obj/item/holder
+ turns_per_wander = 5
diff --git a/code/modules/mob/living/simple_animal/aquatic/_aquatic_retaliate.dm b/code/modules/mob/living/simple_animal/aquatic/_aquatic_retaliate.dm
index 64143d3de6e..21458aa61cf 100644
--- a/code/modules/mob/living/simple_animal/aquatic/_aquatic_retaliate.dm
+++ b/code/modules/mob/living/simple_animal/aquatic/_aquatic_retaliate.dm
@@ -1,12 +1,11 @@
/mob/living/simple_animal/hostile/retaliate/aquatic
abstract_type = /mob/living/simple_animal/hostile/retaliate/aquatic
icon = 'icons/mob/simple_animal/fish_content.dmi'
- turns_per_move = 5
natural_weapon = /obj/item/natural_weapon/bite
- speed = 4
mob_size = MOB_SIZE_MEDIUM
emote_see = list("gnashes")
base_animal_type = /mob/living/simple_animal/aquatic // used for language, ignore actual type
is_aquatic = TRUE
butchery_data = /decl/butchery_data/animal/fish
holder_type = /obj/item/holder
+ turns_per_wander = 5
diff --git a/code/modules/mob/living/simple_animal/aquatic/aquatic_sharks.dm b/code/modules/mob/living/simple_animal/aquatic/aquatic_sharks.dm
index 747b5605685..5645b465835 100644
--- a/code/modules/mob/living/simple_animal/aquatic/aquatic_sharks.dm
+++ b/code/modules/mob/living/simple_animal/aquatic/aquatic_sharks.dm
@@ -15,10 +15,12 @@
name = "gigacretoxyrhina"
desc = "That is a lot of shark."
icon = 'icons/mob/simple_animal/spaceshark.dmi'
- turns_per_move = 2
- move_to_delay = 2
+ move_intents = list(
+ /decl/move_intent/walk/animal,
+ /decl/move_intent/run/animal
+ )
+ turns_per_wander = 2
attack_same = 1
- speed = 0
mob_size = MOB_SIZE_LARGE
pixel_x = -16
max_health = 400
diff --git a/code/modules/mob/living/simple_animal/crow/crow.dm b/code/modules/mob/living/simple_animal/crow/crow.dm
index 7120590ae10..cebe755f81c 100644
--- a/code/modules/mob/living/simple_animal/crow/crow.dm
+++ b/code/modules/mob/living/simple_animal/crow/crow.dm
@@ -21,7 +21,7 @@
natural_weapon = /obj/item/natural_weapon/crow_claws
- stop_automated_movement = TRUE
+ stop_wandering = TRUE
universal_speak = TRUE
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index eab529e073c..f0623130d0e 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -8,7 +8,7 @@
emote_hear = list("meows","mews")
emote_see = list("shakes their head", "shivers")
speak_chance = 0.5
- turns_per_move = 5
+ turns_per_wander = 5
see_in_dark = 6
minbodytemp = 223 //Below -50 Degrees Celsius
maxbodytemp = 323 //Above 50 Degrees Celsius
@@ -46,7 +46,7 @@
M.splat()
visible_emote(pick("bites \the [M]!","toys with \the [M].","chomps on \the [M]!"))
movement_target = null
- stop_automated_movement = 0
+ stop_wandering = FALSE
break
for(var/mob/living/simple_animal/passive/mouse/snack in oview(src,5))
@@ -56,7 +56,7 @@
turns_since_scan++
if (turns_since_scan > 5)
- walk_to(src,0)
+ stop_automove()
turns_since_scan = 0
if (flee_target) //fleeing takes precendence
@@ -80,30 +80,33 @@
//if our target is neither inside a turf or inside a human(???), stop
if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) ))
movement_target = null
- stop_automated_movement = 0
+ stop_wandering = FALSE
//if we have no target or our current one is out of sight/too far away
if( !movement_target || !(movement_target.loc in oview(src, 4)) )
movement_target = null
- stop_automated_movement = 0
+ stop_wandering = FALSE
for(var/mob/living/simple_animal/passive/mouse/snack in oview(src)) //search for a new target
if(isturf(snack.loc) && !snack.stat)
movement_target = snack
break
if(movement_target)
- stop_automated_movement = 1
- walk_to(src,movement_target,0,3)
+ set_moving_quickly()
+ stop_wandering = TRUE
+ start_automove(movement_target)
/mob/living/simple_animal/cat/proc/handle_flee_target()
//see if we should stop fleeing
if (flee_target && !(flee_target.loc in view(src)))
flee_target = null
- stop_automated_movement = 0
+ stop_wandering = FALSE
if (flee_target)
if(prob(25)) say("HSSSSS")
- stop_automated_movement = 1
- walk_away(src, flee_target, 7, 2)
+ set_moving_quickly()
+ stop_wandering = TRUE
+ start_automove(flee_target, metadata = global._flee_automove_metadata)
+
/mob/living/simple_animal/cat/proc/set_flee_target(atom/A)
if(A)
@@ -137,32 +140,41 @@
var/mob/living/human/friend
var/befriend_job = null
+/mob/living/simple_animal/cat/fluff
+ var/follow_dist = 4
+
+/mob/living/simple_animal/cat/fluff/get_acceptable_automove_distance_from_target()
+ . = follow_dist
+ if(friend)
+ if(friend.stat >= DEAD || friend.is_asystole()) //danger
+ . = 1
+ else if (friend.stat || friend.current_health <= 50) //danger or just sleeping
+ . = 2
+ else
+ . = follow_dist
+ return max(. - 2, 1)
+
/mob/living/simple_animal/cat/fluff/handle_movement_target()
if (!QDELETED(friend))
- var/follow_dist = 4
- if (friend.stat >= DEAD || friend.is_asystole()) //danger
- follow_dist = 1
- else if (friend.stat || friend.current_health <= 50) //danger or just sleeping
- follow_dist = 2
- var/near_dist = max(follow_dist - 2, 1)
+ var/near_dist = get_acceptable_automove_distance_from_target()
var/current_dist = get_dist(src, friend)
if (movement_target != friend)
if (current_dist > follow_dist && !ismouse(movement_target) && (friend in oview(src)))
//stop existing movement
- walk_to(src,0)
+ stop_automove()
turns_since_scan = 0
//walk to friend
- stop_automated_movement = 1
+ stop_wandering = TRUE
movement_target = friend
- walk_to(src, movement_target, near_dist, 4)
+ start_automove(movement_target)
//already following and close enough, stop
else if (current_dist <= near_dist)
- walk_to(src,0)
+ stop_automove()
movement_target = null
- stop_automated_movement = 0
+ stop_wandering = FALSE
if (prob(10))
say("Meow!")
diff --git a/code/modules/mob/living/simple_animal/friendly/corgi.dm b/code/modules/mob/living/simple_animal/friendly/corgi.dm
index 6d021ba10ae..f216fb447de 100644
--- a/code/modules/mob/living/simple_animal/friendly/corgi.dm
+++ b/code/modules/mob/living/simple_animal/friendly/corgi.dm
@@ -9,7 +9,7 @@
emote_hear = list("barks", "woofs", "yaps","pants")
emote_see = list("shakes its head", "shivers")
speak_chance = 0.5
- turns_per_move = 10
+ turns_per_wander = 10
response_disarm = "bops"
see_in_dark = 5
mob_size = MOB_SIZE_SMALL
@@ -52,16 +52,16 @@
turns_since_scan = 0
if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) ))
movement_target = null
- stop_automated_movement = 0
+ stop_wandering = FALSE
if( !movement_target || !(movement_target.loc in oview(src, 3)) )
movement_target = null
- stop_automated_movement = 0
+ stop_wandering = FALSE
for(var/obj/item/chems/food/S in oview(src,3))
if(isturf(S.loc) || ishuman(S.loc))
movement_target = S
break
if(movement_target)
- stop_automated_movement = 1
+ stop_wandering = TRUE
step_to(src,movement_target,1)
sleep(3)
step_to(src,movement_target,1)
diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm
index fc2d285dc30..5b7c22f062c 100644
--- a/code/modules/mob/living/simple_animal/friendly/crab.dm
+++ b/code/modules/mob/living/simple_animal/friendly/crab.dm
@@ -8,16 +8,16 @@
emote_hear = list("clicks")
emote_see = list("clacks")
speak_chance = 0.5
- turns_per_move = 5
+ turns_per_wander = 5
response_harm = "stamps on"
- stop_automated_movement = 1
+ stop_wandering = TRUE
possession_candidate = 1
can_escape = TRUE //snip snip
pass_flags = PASS_FLAG_TABLE
natural_armor = list(
ARMOR_MELEE = ARMOR_MELEE_KNIVES
)
- ai = /datum/ai/crab
+ ai = /datum/mob_controller/crab
butchery_data = /decl/butchery_data/animal/arthropod/crab
// TODO
@@ -27,6 +27,9 @@
bodytype_flag = 0
bodytype_category = "hexapodal animal body"
+/decl/bodytype/hexapod/get_ignited_icon_state(mob/living/victim)
+ return "Generic_mob_burning"
+
/mob/living/simple_animal/crab/get_bodytype()
return GET_DECL(/decl/bodytype/hexapod/animal/crab)
@@ -41,18 +44,18 @@
)
. = ..()
-/datum/ai/crab
+/datum/mob_controller/crab
expected_type = /mob/living/simple_animal/crab
-/datum/ai/crab/do_process(time_elapsed)
+/datum/mob_controller/crab/do_process(time_elapsed)
. = ..()
var/mob/living/simple_animal/crab/crab = body
if(!isturf(crab.loc) || crab.current_posture.prone || crab.buckled)
return
- crab.turns_since_move++
- if(crab.turns_since_move >= crab.turns_per_move)
+ crab.turns_since_wander++
+ if(crab.turns_since_wander >= crab.turns_per_wander)
crab.Move(get_step(crab,pick(4,8)))
- crab.turns_since_move = 0
+ crab.turns_since_wander = 0
//COFFEE! SQUEEEEEEEEE!
/mob/living/simple_animal/crab/Coffee
diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
index 4f126c14998..3b535efe090 100644
--- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
+++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
@@ -8,19 +8,19 @@
emote_hear = list("brays")
emote_see = list("shakes its head", "stamps a foot", "glares around")
speak_chance = 0.5
- turns_per_move = 5
+ turns_per_wander = 5
see_in_dark = 6
faction = "goat"
max_health = 40
natural_weapon = /obj/item/natural_weapon/hooves
butchery_data = /decl/butchery_data/animal/ruminant/goat
- ai = /datum/ai/goat
+ ai = /datum/mob_controller/goat
var/datum/reagents/udder = null
-/datum/ai/goat
+/datum/mob_controller/goat
expected_type = /mob/living/simple_animal/hostile/retaliate/goat
-/datum/ai/goat/do_process(time_elapsed)
+/datum/mob_controller/goat/do_process(time_elapsed)
//chance to go crazy and start wacking stuff
var/mob/living/simple_animal/hostile/retaliate/goat/goat = body
if(!length(goat.enemies) && prob(1))
@@ -91,7 +91,7 @@
emote_hear = list("brays")
emote_see = list("shakes its head")
speak_chance = 0.5
- turns_per_move = 5
+ turns_per_wander = 5
see_in_dark = 6
max_health = 50
butchery_data = /decl/butchery_data/animal/ruminant/cow
@@ -156,7 +156,7 @@
emote_hear = list("cheeps")
emote_see = list("pecks at the ground","flaps its tiny wings")
speak_chance = 1
- turns_per_move = 2
+ turns_per_wander = 2
max_health = 1
pass_flags = PASS_FLAG_TABLE | PASS_FLAG_GRILLE
mob_size = MOB_SIZE_MINISCULE
@@ -183,7 +183,7 @@
mob_size = MOB_SIZE_SMALL
butchery_data = /decl/butchery_data/animal/small/fowl
speak_chance = 2
- turns_per_move = 3
+ turns_per_wander = 3
abstract_type = /mob/living/simple_animal/fowl
var/body_color
diff --git a/code/modules/mob/living/simple_animal/friendly/koala.dm b/code/modules/mob/living/simple_animal/friendly/koala.dm
index 773b78c75db..78b96516f2d 100644
--- a/code/modules/mob/living/simple_animal/friendly/koala.dm
+++ b/code/modules/mob/living/simple_animal/friendly/koala.dm
@@ -4,12 +4,11 @@
desc = "A little grey bear. How long is he gonna sleep today?"
icon = 'icons/mob/simple_animal/koala.dmi'
max_health = 45
- speed = 4
speak_emote = list("roar")
emote_speech = list("Rrr", "Wraarh...", "Pfrrr...")
emote_hear = list("grunting.","rustling.", "slowly yawns.")
emote_see = list("slowly turns around his head.", "rises to his feet, and lays to the ground on all fours.")
speak_chance = 0.5
- turns_per_move = 10 //lazy
+ turns_per_wander = 10 //lazy
see_in_dark = 6
- stop_automated_movement_when_pulled = 1
+ stop_wandering_when_pulled = TRUE
diff --git a/code/modules/mob/living/simple_animal/friendly/mushroom.dm b/code/modules/mob/living/simple_animal/friendly/mushroom.dm
index d7950196414..e8a6cafc2f4 100644
--- a/code/modules/mob/living/simple_animal/friendly/mushroom.dm
+++ b/code/modules/mob/living/simple_animal/friendly/mushroom.dm
@@ -4,7 +4,7 @@
icon = 'icons/mob/simple_animal/mushroom.dmi'
mob_size = MOB_SIZE_SMALL
speak_chance = 0
- turns_per_move = 1
+ turns_per_wander = 1
max_health = 5
harm_intent_damage = 5
pass_flags = PASS_FLAG_TABLE
diff --git a/code/modules/mob/living/simple_animal/friendly/possum.dm b/code/modules/mob/living/simple_animal/friendly/possum.dm
index 5fdc3e52708..9ffb5aba8f5 100644
--- a/code/modules/mob/living/simple_animal/friendly/possum.dm
+++ b/code/modules/mob/living/simple_animal/friendly/possum.dm
@@ -9,7 +9,7 @@
emote_see = list("forages for trash", "lounges")
pass_flags = PASS_FLAG_TABLE
speak_chance = 0.5
- turns_per_move = 3
+ turns_per_wander = 3
see_in_dark = 6
max_health = 50
response_harm = "stamps on"
@@ -24,13 +24,13 @@
can_pull_size = ITEM_SIZE_SMALL
can_pull_mobs = MOB_PULL_SMALLER
holder_type = /obj/item/holder
- ai = /datum/ai/opossum
+ ai = /datum/mob_controller/opossum
var/is_angry = FALSE
-/datum/ai/opossum
+/datum/mob_controller/opossum
expected_type = /mob/living/simple_animal/opossum
-/datum/ai/opossum/do_process(time_elapsed)
+/datum/mob_controller/opossum/do_process(time_elapsed)
. = ..()
if(!prob(1))
return
diff --git a/code/modules/mob/living/simple_animal/friendly/tomato.dm b/code/modules/mob/living/simple_animal/friendly/tomato.dm
index 8d6a2d42c95..46d75b329e8 100644
--- a/code/modules/mob/living/simple_animal/friendly/tomato.dm
+++ b/code/modules/mob/living/simple_animal/friendly/tomato.dm
@@ -3,7 +3,7 @@
desc = "It's a horrifyingly enormous beef tomato, and it's packing extra beef!"
icon = 'icons/mob/simple_animal/tomato.dmi'
speak_chance = 0
- turns_per_move = 5
+ turns_per_wander = 5
max_health = 15
response_help_3p = "$USER$ pokes $TARGET$."
response_help_1p = "You poke $TARGET$."
diff --git a/code/modules/mob/living/simple_animal/hostile/antlion.dm b/code/modules/mob/living/simple_animal/hostile/antlion.dm
index 5328aa7f82c..c68a07d86e5 100644
--- a/code/modules/mob/living/simple_animal/hostile/antlion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/antlion.dm
@@ -73,7 +73,7 @@
heal_overall_damage(rand(heal_amount), rand(heal_amount))
/mob/living/simple_animal/hostile/antlion/proc/prep_burrow(var/new_bool)
- stop_automated_movement = new_bool
+ stop_wandering = new_bool
stop_automation = new_bool
healing = new_bool
diff --git a/code/modules/mob/living/simple_animal/hostile/bad_drone.dm b/code/modules/mob/living/simple_animal/hostile/bad_drone.dm
index f4e8703919a..cd0db4b4492 100644
--- a/code/modules/mob/living/simple_animal/hostile/bad_drone.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bad_drone.dm
@@ -11,7 +11,6 @@
min_gas = null
max_gas = null
minbodytemp = 0
- speed = 4
mob_size = MOB_SIZE_TINY
gene_damage = -1
attack_delay = DEFAULT_QUICK_COOLDOWN
diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm
index c378abffc2e..a347b1f0c61 100644
--- a/code/modules/mob/living/simple_animal/hostile/bat.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bat.dm
@@ -3,8 +3,7 @@
desc = "A swarm of cute little blood sucking bats - they look pretty upset."
icon = 'icons/mob/simple_animal/bats.dmi'
speak_chance = 0
- turns_per_move = 3
- speed = 4
+ turns_per_wander = 3
max_health = 20
harm_intent_damage = 8
natural_weapon = /obj/item/natural_weapon/bite
diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm
index 82ab25346f1..d2003549d3d 100644
--- a/code/modules/mob/living/simple_animal/hostile/bear.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bear.dm
@@ -8,10 +8,10 @@
emote_hear = list("rawrs","grumbles","grawls")
emote_see = list("stares ferociously", "stomps")
speak_chance = 0.5
- turns_per_move = 5
+ turns_per_wander = 5
see_in_dark = 6
response_harm = "pokes"
- stop_automated_movement_when_pulled = 0
+ stop_wandering_when_pulled = FALSE
max_health = 60
natural_weapon = /obj/item/natural_weapon/claws/strong
can_escape = TRUE
@@ -41,7 +41,7 @@
switch(stance)
if(HOSTILE_STANCE_TIRED)
- stop_automated_movement = 1
+ stop_wandering = TRUE
stance_step++
if(stance_step >= 10) //rests for 10 ticks
if(target_mob && (target_mob in ListTargets(10)))
@@ -50,7 +50,7 @@
stance = HOSTILE_STANCE_IDLE
if(HOSTILE_STANCE_ALERT)
- stop_automated_movement = 1
+ stop_wandering = TRUE
var/found_mob = 0
if(target_mob && (target_mob in ListTargets(10)))
if(!(SA_attackable(target_mob)))
@@ -76,7 +76,7 @@
custom_emote(1, "is worn out and needs to rest." )
stance = HOSTILE_STANCE_TIRED
stance_step = 0
- walk(src, 0) //This stops the bear's walking
+ stop_automove()
return
diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm
index b01a3517ef2..b2a0e9e1825 100644
--- a/code/modules/mob/living/simple_animal/hostile/carp.dm
+++ b/code/modules/mob/living/simple_animal/hostile/carp.dm
@@ -3,13 +3,13 @@
desc = "A ferocious, fang-bearing creature that resembles a fish."
icon = 'icons/mob/simple_animal/space_carp.dmi'
speak_chance = 0
- turns_per_move = 3
- speed = 2
max_health = 50
+ turns_per_wander = 3
harm_intent_damage = 8
natural_weapon = /obj/item/natural_weapon/bite
pry_time = 10 SECONDS
pry_desc = "biting"
+ base_movement_delay = 2
//Space carp aren't affected by atmos.
min_gas = null
@@ -22,6 +22,7 @@
butchery_data = /decl/butchery_data/animal/fish/space_carp
var/carp_color = COLOR_PURPLE
+
/mob/living/simple_animal/hostile/carp/Initialize()
. = ..()
carp_randomify()
diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm b/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm
index 87687c40fcf..86d6aad165e 100644
--- a/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm
+++ b/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm
@@ -3,7 +3,7 @@
stance = COMMANDED_STOP
natural_weapon = /obj/item/natural_weapon
density = FALSE
- ai = /datum/ai/commanded
+ ai = /datum/mob_controller/commanded
var/list/command_buffer = list()
var/list/known_commands = list("stay", "stop", "attack", "follow")
var/mob/master = null //undisputed master. Their commands hold ultimate sway and ultimate power.
@@ -22,10 +22,10 @@
command_buffer.Add(lowertext(html_decode(message)))
return 0
-/datum/ai/commanded
+/datum/mob_controller/commanded
expected_type = /mob/living/simple_animal/hostile/commanded
-/datum/ai/commanded/do_process(time_elapsed)
+/datum/mob_controller/commanded/do_process(time_elapsed)
..()
var/mob/living/simple_animal/hostile/commanded/com = body
while(com.command_buffer.len > 1)
@@ -69,11 +69,12 @@
/mob/living/simple_animal/hostile/commanded/proc/follow_target()
- stop_automated_movement = 1
+ stop_wandering = TRUE
if(!target_mob)
return
if(target_mob in ListTargets(10))
- walk_to(src,target_mob,1,move_to_delay)
+ set_moving_slowly()
+ start_automove(target_mob)
/mob/living/simple_animal/hostile/commanded/proc/commanded_stop() //basically a proc that runs whenever we are asked to stay put. Probably going to remain unused.
return
@@ -123,7 +124,7 @@
/mob/living/simple_animal/hostile/commanded/proc/attack_command(var/mob/speaker,var/text)
target_mob = null //want me to attack something? Well I better forget my old target.
- walk_to(src,0)
+ stop_automove()
stance = HOSTILE_STANCE_IDLE
if(text == "attack" || findtext(text,"everyone") || findtext(text,"anybody") || findtext(text, "somebody") || findtext(text, "someone")) //if its just 'attack' then just attack anybody, same for if they say 'everyone', somebody, anybody. Assuming non-pickiness.
allowed_targets = list("everyone")//everyone? EVERYONE
@@ -136,16 +137,16 @@
/mob/living/simple_animal/hostile/commanded/proc/stay_command(var/mob/speaker,var/text)
target_mob = null
stance = COMMANDED_STOP
- stop_automated_movement = 1
- walk_to(src,0)
+ stop_wandering = TRUE
+ stop_automove()
return 1
/mob/living/simple_animal/hostile/commanded/proc/stop_command(var/mob/speaker,var/text)
allowed_targets = list()
- walk_to(src,0)
+ stop_automove()
target_mob = null //gotta stop SOMETHIN
stance = HOSTILE_STANCE_IDLE
- stop_automated_movement = 0
+ stop_wandering = FALSE
return 1
/mob/living/simple_animal/hostile/commanded/proc/follow_command(var/mob/speaker,var/text)
diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/nanomachines.dm b/code/modules/mob/living/simple_animal/hostile/commanded/nanomachines.dm
index edeaaf07e0a..c5fd7cf7bf7 100644
--- a/code/modules/mob/living/simple_animal/hostile/commanded/nanomachines.dm
+++ b/code/modules/mob/living/simple_animal/hostile/commanded/nanomachines.dm
@@ -14,7 +14,7 @@
response_help_3p = "$USER$ waves $USER_THEIR$ hand through $TARGET$."
response_harm = "agitates"
response_disarm = "fans at"
- ai = /datum/ai/nanomachines
+ ai = /datum/mob_controller/nanomachines
var/regen_time = 0
var/emergency_protocols = 0
@@ -25,10 +25,10 @@
force = 2
sharp = TRUE
-/datum/ai/nanomachines
+/datum/mob_controller/nanomachines
expected_type = /mob/living/simple_animal/hostile/commanded/nanomachine
-/datum/ai/nanomachines/do_process(time_elapsed)
+/datum/mob_controller/nanomachines/do_process(time_elapsed)
. = ..()
var/mob/living/simple_animal/hostile/commanded/nanomachine/swarm = body
switch(swarm.stance)
@@ -63,7 +63,8 @@
/mob/living/simple_animal/hostile/commanded/nanomachine/proc/move_to_heal()
if(!target_mob)
return 0
- walk_to(src,target_mob,1,move_to_delay)
+ set_moving_quickly()
+ start_automove(target_mob)
if(Adjacent(target_mob))
stance = COMMANDED_HEALING
diff --git a/code/modules/mob/living/simple_animal/hostile/creature.dm b/code/modules/mob/living/simple_animal/hostile/creature.dm
index c818a6512ab..83edfc003cf 100644
--- a/code/modules/mob/living/simple_animal/hostile/creature.dm
+++ b/code/modules/mob/living/simple_animal/hostile/creature.dm
@@ -6,5 +6,4 @@
max_health = 100
natural_weapon = /obj/item/natural_weapon/bite/strong
faction = "creature"
- speed = 4
supernatural = 1
diff --git a/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm b/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm
index 102b030899d..bbce86503bd 100644
--- a/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm
+++ b/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm
@@ -6,7 +6,7 @@
max_health = 100
natural_weapon = /obj/item/natural_weapon/bite/strong
density = FALSE
- stop_automated_movement = 1
+ stop_wandering = TRUE
wander = FALSE
anchored = TRUE
faction = "cute ghost dogs"
diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm
index b5f09072942..2dff73948a7 100644
--- a/code/modules/mob/living/simple_animal/hostile/faithless.dm
+++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm
@@ -3,20 +3,16 @@
desc = "The Wish Granter's faith in humanity, incarnate"
icon = 'icons/mob/simple_animal/faithless.dmi'
speak_chance = 0
- turns_per_move = 5
+ turns_per_wander = 5
response_help_1p = "You wave your hand through $TARGET$."
response_help_3p = "$USER$ waves $USER_THEIR$ hand through $TARGET$."
max_health = 80
gene_damage = -1
-
harm_intent_damage = 10
natural_weapon = /obj/item/natural_weapon/faithless
-
min_gas = null
max_gas = null
minbodytemp = 0
- speed = 4
-
faction = "faithless"
supernatural = 1
diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
index 8547d0d94ad..43dde9826e5 100644
--- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
+++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
@@ -12,7 +12,7 @@
emote_hear = list("chitters")
emote_see = list("rubs its forelegs together", "wipes its fangs", "stops suddenly")
speak_chance = 2.5
- turns_per_move = 5
+ turns_per_wander = 5
see_in_dark = 10
response_harm = "pokes"
max_health = 125
@@ -21,8 +21,10 @@
cold_damage_per_tick = 20
faction = "spiders"
pass_flags = PASS_FLAG_TABLE
- move_to_delay = 3
- speed = 1
+ move_intents = list(
+ /decl/move_intent/walk/animal,
+ /decl/move_intent/run/animal
+ )
max_gas = list(
/decl/material/gas/chlorine = 1,
/decl/material/gas/carbon_dioxide = 5,
@@ -35,7 +37,8 @@
base_animal_type = /mob/living/simple_animal/hostile/giant_spider
butchery_data = /decl/butchery_data/animal/arthropod/giant_spider
glowing_eyes = TRUE
- ai = /datum/ai/giant_spider
+ ai = /datum/mob_controller/giant_spider
+ base_movement_delay = 1
var/poison_per_bite = 6
var/poison_type = /decl/material/liquid/venom
@@ -60,10 +63,13 @@
max_health = 200
natural_weapon = /obj/item/natural_weapon/bite/strong
poison_per_bite = 5
- speed = 2
- move_to_delay = 4
+ move_intents = list(
+ /decl/move_intent/walk/animal_slow,
+ /decl/move_intent/run/animal_slow
+ )
break_stuff_probability = 15
pry_time = 6 SECONDS
+ base_movement_delay = 2
var/vengance
var/berserking
@@ -76,10 +82,10 @@
max_health = 80
harm_intent_damage = 6 //soft
poison_per_bite = 5
- speed = 0
poison_type = /decl/material/liquid/sedatives
break_stuff_probability = 10
pry_time = 9 SECONDS
+ base_movement_delay = 0
var/atom/cocoon_target
var/fed = 0
@@ -100,8 +106,10 @@
max_health = 150
natural_weapon = /obj/item/natural_weapon/bite/strong
poison_per_bite = 10
- speed = -1
- move_to_delay = 2
+ move_intents = list(
+ /decl/move_intent/walk/animal_fast,
+ /decl/move_intent/run/animal_fast
+ )
break_stuff_probability = 30
hunt_chance = 25
can_escape = TRUE
@@ -110,7 +118,7 @@
does_spin = FALSE
available_maneuvers = list(/decl/maneuver/leap/spider)
ability_cooldown = 3 MINUTES
-
+ base_movement_delay = 0
var/leap_range = 5
//spitters - fast, comparatively weak, very venomous; projectile attacks but will resort to melee once out of ammo
@@ -120,7 +128,10 @@
max_health = 90
poison_per_bite = 15
ranged = TRUE
- move_to_delay = 2
+ move_intents = list(
+ /decl/move_intent/walk/animal_fast,
+ /decl/move_intent/run/animal_fast
+ )
projectiletype = /obj/item/projectile/venom
projectilesound = 'sound/effects/hypospray.ogg'
fire_desc = "spits venom"
@@ -164,21 +175,22 @@
if(prob(poison_per_bite))
to_chat(L, "You feel a tiny prick.")
-/datum/ai/giant_spider
+/datum/mob_controller/giant_spider
expected_type = /mob/living/simple_animal/hostile/giant_spider
-/datum/ai/giant_spider/do_process()
+/datum/mob_controller/giant_spider/do_process()
var/mob/living/simple_animal/hostile/giant_spider/spooder = body
if(spooder.stance == HOSTILE_STANCE_IDLE)
//chance to skitter madly away
if(!spooder.busy && prob(spooder.hunt_chance))
- spooder.stop_automated_movement = 1
- walk_to(spooder, pick(orange(20, spooder)), 1, spooder.move_to_delay)
- addtimer(CALLBACK(spooder, TYPE_PROC_REF(/mob/living/simple_animal/hostile/giant_spider, disable_stop_automated_movement)), 5 SECONDS)
-
-/mob/living/simple_animal/hostile/giant_spider/proc/disable_stop_automated_movement()
- stop_automated_movement = 0
- walk(src,0)
+ spooder.stop_wandering = TRUE
+ spooder.set_moving_quickly()
+ spooder.start_automove(pick(orange(20, spooder)))
+ addtimer(CALLBACK(spooder, TYPE_PROC_REF(/mob/living/simple_animal/hostile/giant_spider, disable_stop_automove)), 5 SECONDS)
+
+/mob/living/simple_animal/hostile/giant_spider/proc/disable_stop_automove()
+ stop_wandering = FALSE
+ stop_automove()
kick_stance()
/mob/living/simple_animal/hostile/giant_spider/proc/divorce()
@@ -188,12 +200,12 @@
Guard caste procs
****************/
/mob/living/simple_animal/hostile/giant_spider/guard
- ai = /datum/ai/giant_spider/guard
+ ai = /datum/mob_controller/giant_spider/guard
-/datum/ai/giant_spider/guard
+/datum/mob_controller/giant_spider/guard
expected_type = /mob/living/simple_animal/hostile/giant_spider/guard
-/datum/ai/giant_spider/guard/do_process(time_elapsed)
+/datum/mob_controller/giant_spider/guard/do_process(time_elapsed)
. = ..()
var/mob/living/simple_animal/hostile/giant_spider/guard/spooder = body
if(spooder.berserking)
@@ -227,16 +239,17 @@ Guard caste procs
return 1
/mob/living/simple_animal/hostile/giant_spider/guard/proc/protect(mob/nurse)
- stop_automated_movement = 1
- walk_to(src, nurse, 2, move_to_delay)
- addtimer(CALLBACK(src, TYPE_PROC_REF(/mob/living/simple_animal/hostile/giant_spider, disable_stop_automated_movement)), 5 SECONDS)
+ stop_wandering = TRUE
+ var/static/datum/automove_metadata/_spider_guard_metadata = new(_acceptable_distance = 2)
+ start_automove(nurse, metadata = _spider_guard_metadata)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/mob/living/simple_animal/hostile/giant_spider, disable_stop_automove)), 5 SECONDS)
/mob/living/simple_animal/hostile/giant_spider/guard/proc/go_berserk()
audible_message("\The [src] chitters wildly!")
var/obj/item/attacking_with = get_natural_weapon()
if(attacking_with)
attacking_with.force = initial(attacking_with.force) + 5
- move_to_delay--
+ set_moving_quickly()
break_stuff_probability = 45
addtimer(CALLBACK(src, PROC_REF(calm_down)), 3 MINUTES)
@@ -246,7 +259,7 @@ Guard caste procs
var/obj/item/attacking_with = get_natural_weapon()
if(attacking_with)
attacking_with.force = initial(attacking_with.force)
- move_to_delay++
+ set_moving_slowly()
break_stuff_probability = 10
/****************
@@ -290,20 +303,20 @@ Nurse caste procs
if(cocoon_target == C && get_dist(src,cocoon_target) > 1)
cocoon_target = null
busy = 0
- stop_automated_movement = 0
+ stop_wandering = FALSE
/mob/living/simple_animal/hostile/giant_spider/nurse
- ai = /datum/ai/giant_spider/nurse
+ ai = /datum/mob_controller/giant_spider/nurse
-/datum/ai/giant_spider/nurse
+/datum/mob_controller/giant_spider/nurse
expected_type = /mob/living/simple_animal/hostile/giant_spider/nurse
-/datum/ai/giant_spider/nurse/do_process(time_elapsed)
+/datum/mob_controller/giant_spider/nurse/do_process(time_elapsed)
. = ..()
var/mob/living/simple_animal/hostile/giant_spider/nurse/spooder = body
if(spooder.stance != HOSTILE_STANCE_IDLE)
spooder.busy = 0
- spooder.stop_automated_movement = 0
+ spooder.stop_wandering = FALSE
return
var/list/can_see = view(spooder, 10)
@@ -316,7 +329,7 @@ Nurse caste procs
if(web_target.stat)
spooder.cocoon_target = web_target
spooder.busy = MOVING_TO_TARGET
- walk_to(spooder, web_target, 1, spooder.move_to_delay)
+ spooder.start_automove(web_target)
//give up if we can't reach them after 10 seconds
spooder.GiveUp(web_target)
return
@@ -326,19 +339,19 @@ Nurse caste procs
if(!W)
spooder.busy = SPINNING_WEB
spooder.visible_message(SPAN_NOTICE("\The [spooder] begins to secrete a sticky substance."))
- spooder.stop_automated_movement = 1
+ spooder.stop_wandering = TRUE
spawn(4 SECONDS)
if(spooder.busy == SPINNING_WEB)
new /obj/effect/spider/stickyweb(spooder.loc)
spooder.busy = 0
- spooder.stop_automated_movement = 0
+ spooder.stop_wandering = FALSE
else
//third, lay an egg cluster there
var/obj/effect/spider/eggcluster/E = locate() in get_turf(spooder)
if(!E && spooder.fed > 0 && spooder.max_eggs)
spooder.busy = LAYING_EGGS
spooder.visible_message(SPAN_NOTICE("\The [spooder] begins to lay a cluster of eggs."))
- spooder.stop_automated_movement = 1
+ spooder.stop_wandering = TRUE
spawn(5 SECONDS)
if(spooder.busy == LAYING_EGGS)
E = locate() in get_turf(spooder)
@@ -347,7 +360,7 @@ Nurse caste procs
spooder.max_eggs--
spooder.fed--
spooder.busy = 0
- spooder.stop_automated_movement = 0
+ spooder.stop_wandering = FALSE
else
//fourthly, cocoon any nearby items so those pesky pinkskins can't use them
for(var/obj/O in can_see)
@@ -361,8 +374,8 @@ Nurse caste procs
if(istype(O, /obj/item) || istype(O, /obj/structure) || istype(O, /obj/machinery))
spooder.cocoon_target = O
spooder.busy = MOVING_TO_TARGET
- spooder.stop_automated_movement = 1
- walk_to(spooder, O, 1, spooder.move_to_delay)
+ spooder.stop_wandering = TRUE
+ spooder.start_automove(O)
//give up if we can't reach them after 10 seconds
spooder.GiveUp(O)
@@ -370,8 +383,8 @@ Nurse caste procs
if(spooder.Adjacent(spooder.cocoon_target))
spooder.busy = SPINNING_COCOON
spooder.visible_message(SPAN_NOTICE("\The [spooder] begins to secrete a sticky substance around \the [spooder.cocoon_target]."))
- spooder.stop_automated_movement = 1
- walk(spooder,0)
+ spooder.stop_wandering = TRUE
+ spooder.stop_automove()
spawn(5 SECONDS)
if(spooder.busy == SPINNING_COCOON)
if(spooder.cocoon_target && isturf(spooder.cocoon_target.loc) && get_dist(spooder, spooder.cocoon_target) <= 1)
@@ -399,7 +412,7 @@ Nurse caste procs
if(large_cocoon)
C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3")
spooder.busy = 0
- spooder.stop_automated_movement = 0
+ spooder.stop_wandering = FALSE
/*****************
Hunter caste procs
@@ -415,7 +428,7 @@ Hunter caste procs
/mob/living/simple_animal/hostile/giant_spider/hunter/perform_maneuver(var/maneuver, var/atom/target)
if(!isliving(target) || get_dist(src, target) <= 3)
return FALSE
- walk(src,0)
+ stop_automove()
var/first_stop_automation
if(stop_automation)
first_stop_automation = stop_automation
diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm
index 9572adf81c7..f10abe8a1c4 100644
--- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm
@@ -10,7 +10,6 @@
min_gas = null
max_gas = null
minbodytemp = 0
- speed = 4
natural_armor = list(
ARMOR_MELEE = ARMOR_MELEE_KNIVES
)
@@ -25,7 +24,7 @@
/mob/living/simple_animal/hostile/hivebot/range
desc = "A junky looking robot with four spiky legs. It's equipped with some kind of small-bore gun."
ranged = 1
- speed = 7
+ base_movement_delay = 7
/mob/living/simple_animal/hostile/hivebot/rapid
ranged = 1
@@ -83,7 +82,6 @@ The megabot
icon = 'icons/mob/simple_animal/megabot.dmi'
max_health = 440
natural_weapon = /obj/item/natural_weapon/circular_saw
- speed = 0
natural_armor = list(
ARMOR_MELEE = ARMOR_MELEE_RESISTANT,
ARMOR_BULLET = ARMOR_BALLISTIC_PISTOL
@@ -94,6 +92,7 @@ The megabot
pixel_x = -32
default_pixel_x = -32
+ base_movement_delay = 0
var/attack_mode = ATTACK_MODE_MELEE
var/num_shots
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index c224ef528fd..5b834046448 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -1,11 +1,15 @@
/mob/living/simple_animal/hostile
faction = "hostile"
- stop_automated_movement_when_pulled = 0
+ stop_wandering_when_pulled = FALSE
a_intent = I_HURT
response_help_3p = "$USER$ pokes $TARGET$."
response_help_1p = "You poke $TARGET$."
response_disarm = "shoves"
response_harm = "strikes"
+ move_intents = list(
+ /decl/move_intent/walk/animal_slow,
+ /decl/move_intent/run/animal_slow
+ )
var/stance = HOSTILE_STANCE_IDLE //Used to determine behavior
var/mob/living/target_mob
@@ -17,7 +21,6 @@
var/casingtype
var/fire_desc = "fires" //"X fire_desc at Y!"
var/ranged_range = 6 //tiles of range for ranged attackers to attack
- var/move_to_delay = 4 //delay for the automated movement.
var/list/friends = list()
var/break_stuff_probability = 10
@@ -58,7 +61,7 @@
return null
if(!faction) //No faction, no reason to attack anybody.
return null
- stop_automated_movement = 0
+ stop_wandering = FALSE
for(var/atom/A in ListTargets(10))
var/atom/F = Found(A)
if(F)
@@ -100,9 +103,10 @@
if(!can_act())
return
if(HAS_STATUS(src, STAT_CONFUSE))
- walk_to(src, pick(orange(2, src)), 1, move_to_delay)
+ set_moving_slowly()
+ start_automove(pick(orange(2, src)))
return
- stop_automated_movement = 1
+ stop_wandering = TRUE
if(QDELETED(target_mob) || SA_attackable(target_mob))
stance = HOSTILE_STANCE_IDLE
if(target_mob in ListTargets(10))
@@ -111,13 +115,15 @@
if(!move_only)
OpenFire(target_mob)
else
- walk_to(src, target_mob, 1, move_to_delay)
+ set_moving_quickly()
+ start_automove(target_mob)
else
stance = HOSTILE_STANCE_ATTACKING
- walk_to(src, target_mob, 1, move_to_delay)
+ set_moving_quickly()
+ start_automove(target_mob)
/mob/living/simple_animal/hostile/proc/handle_attacking_target()
- stop_automated_movement = 1
+ stop_wandering = TRUE
if(!target_mob || SA_attackable(target_mob))
LoseTarget()
return 0
@@ -138,11 +144,11 @@
/mob/living/simple_animal/hostile/proc/LoseTarget()
stance = HOSTILE_STANCE_IDLE
target_mob = null
- walk(src, 0)
+ stop_automove()
/mob/living/simple_animal/hostile/proc/LostTarget()
stance = HOSTILE_STANCE_IDLE
- walk(src, 0)
+ stop_automove()
/mob/living/simple_animal/hostile/proc/ListTargets(var/dist = 7)
return hearers(src, dist)-src
@@ -150,12 +156,12 @@
/mob/living/simple_animal/hostile/handle_regular_status_updates()
. = ..()
if(!.)
- walk(src, 0)
+ stop_automove()
/mob/living/simple_animal/hostile/do_delayed_life_action()
..()
if(!can_act())
- walk(src, 0)
+ stop_automove()
kick_stance()
return 0
@@ -180,7 +186,7 @@
else
if(stance != HOSTILE_STANCE_INSIDE)
stance = HOSTILE_STANCE_INSIDE
- walk(src,0)
+ stop_automove()
target_mob = null
/mob/living/simple_animal/hostile/attackby(var/obj/item/O, var/mob/user)
@@ -238,11 +244,11 @@
return TRUE
/mob/living/simple_animal/hostile/proc/DestroySurroundings() //courtesy of Lohikar
- if(!can_act())
+ if(!can_act() || !target_mob)
return
if(prob(break_stuff_probability) && !Adjacent(target_mob))
face_atom(target_mob)
- var/turf/targ = get_step_towards(src, target_mob)
+ var/turf/targ = get_step_resolving_mimic(get_turf(src), get_dir(src, target_mob))
if(!targ)
return
diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm
index efa10e0d964..54f2486779d 100644
--- a/code/modules/mob/living/simple_animal/hostile/mimic.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm
@@ -25,17 +25,17 @@ var/global/list/protected_objects = list(
color = COLOR_STEEL
icon_state = "crate"
butchery_data = null
- speed = 4
max_health = 100
harm_intent_damage = 5
natural_weapon = /obj/item/natural_weapon/bite
min_gas = null
max_gas = null
minbodytemp = 0
-
faction = "mimic"
- move_to_delay = 8
-
+ move_intents = list(
+ /decl/move_intent/walk/animal_very_slow,
+ /decl/move_intent/run/animal_very_slow
+ )
var/weakref/copy_of
var/weakref/creator // the creator
var/destroy_objects = 0
@@ -85,7 +85,29 @@ var/global/list/protected_objects = list(
var/obj/item/I = O
current_health = 15 * I.w_class
attacking_with.force = 2 + initial(I.force)
- move_to_delay = 2 * I.w_class
+
+ if(I.w_class <= ITEM_SIZE_SMALL)
+ move_intents = list(
+ /decl/move_intent/walk/animal_fast,
+ /decl/move_intent/run/animal_fast
+ )
+ else if(I.w_class <= ITEM_SIZE_NO_CONTAINER)
+ move_intents = list(
+ /decl/move_intent/walk/animal,
+ /decl/move_intent/run/animal
+ )
+ else if(I.w_class <= ITEM_SIZE_STRUCTURE)
+ move_intents = list(
+ /decl/move_intent/walk/animal_slow,
+ /decl/move_intent/run/animal_slow
+ )
+ else
+ move_intents = list(
+ /decl/move_intent/walk/animal_very_slow,
+ /decl/move_intent/run/animal_very_slow
+ )
+ move_intent = GET_DECL(move_intents[1])
+
set_max_health(current_health)
if(creator)
@@ -136,7 +158,7 @@ var/global/list/protected_objects = list(
/mob/living/simple_animal/hostile/mimic/sleeping
wander = FALSE
- stop_automated_movement = 1
+ stop_wandering = TRUE
var/awake = 0
diff --git a/code/modules/mob/living/simple_animal/hostile/pike.dm b/code/modules/mob/living/simple_animal/hostile/pike.dm
index c943da673d0..b53847b7527 100644
--- a/code/modules/mob/living/simple_animal/hostile/pike.dm
+++ b/code/modules/mob/living/simple_animal/hostile/pike.dm
@@ -2,10 +2,13 @@
name = "space pike"
desc = "A bigger, angrier cousin of the space carp."
icon = 'icons/mob/simple_animal/spaceshark.dmi'
- turns_per_move = 2
- move_to_delay = 2
+ turns_per_wander = 2
+ move_intents = list(
+ /decl/move_intent/walk/animal_fast,
+ /decl/move_intent/run/animal_fast
+ )
+ base_movement_delay = 1
attack_same = 1
- speed = 1
mob_size = MOB_SIZE_LARGE
offset_overhead_text_x = 16
pixel_x = -16
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm
index 37311dbe032..1d9fe7cac67 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm
@@ -2,14 +2,13 @@
name = "clown"
desc = "A denizen of clown planet"
icon = 'icons/mob/simple_animal/clown.dmi'
- turns_per_move = 5
+ turns_per_wander = 5
emote_speech = list("HONK", "Honk!", "Welcome to clown planet!")
emote_see = list("honks")
speak_chance = 0.5
a_intent = I_HURT
- stop_automated_movement_when_pulled = 0
+ stop_wandering_when_pulled = FALSE
max_health = 75
- speed = -1
harm_intent_damage = 8
can_escape = TRUE
minbodytemp = 270
@@ -19,6 +18,7 @@
unsuitable_atmos_damage = 10
natural_weapon = /obj/item/natural_weapon/clown
faction = "circus"
+ base_movement_delay = -1
/obj/item/natural_weapon/clown
name = "bike horn"
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
index 480645b38c1..7f1994a5e10 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
@@ -7,20 +7,23 @@
ranged = 1
rapid = 0
speak_chance = 2.5
- turns_per_move = 3
+ turns_per_wander = 3
emote_speech = list("ALERT.","Hostile-ile-ile entities dee-twhoooo-wected.","Threat parameterszzzz- szzet.","Bring sub-sub-sub-systems uuuup to combat alert alpha-a-a.")
emote_see = list("beeps menacingly","whirrs threateningly","scans its immediate vicinity")
a_intent = I_HURT
- stop_automated_movement_when_pulled = 0
+ stop_wandering_when_pulled = FALSE
max_health = 300
- speed = 8
- move_to_delay = 6
+ move_intents = list(
+ /decl/move_intent/walk/animal_slow,
+ /decl/move_intent/run/animal_slow
+ )
projectiletype = /obj/item/projectile/beam/drone
projectilesound = 'sound/weapons/laser3.ogg'
destroy_surroundings = 0
gene_damage = -1
butchery_data = /decl/butchery_data/synthetic
bleed_colour = SYNTH_BLOOD_COLOR
+ base_movement_delay = 8
var/datum/effect/effect/system/trail/ion_trail
@@ -131,7 +134,7 @@
else
src.visible_message("[html_icon(src)] [src] suddenly lies still and quiet.")
disabled = rand(150, 600)
- walk(src,0)
+ stop_automove()
if(exploding && prob(20))
if(prob(50))
@@ -144,7 +147,7 @@
exploding = 1
set_stat(UNCONSCIOUS)
wander = TRUE
- walk(src,0)
+ stop_automove()
spawn(rand(50,150))
if(!disabled && exploding)
explosion(get_turf(src), 0, 1, 4, 7)
@@ -168,7 +171,7 @@
take_damage(rand(3,15) * (severity + 1), BURN)
disabled = rand(150, 600)
hostile_drone = 0
- walk(src,0)
+ stop_automove()
/mob/living/simple_animal/hostile/retaliate/malf_drone/get_death_message(gibbed)
return "suddenly breaks apart."
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm
index 0e19309d1f3..92b92b0f5e2 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm
@@ -1,6 +1,6 @@
/mob/living/simple_animal/hostile/retaliate/beast
- ai = /datum/ai/beast
+ ai = /datum/mob_controller/beast
nutrition = 300
var/list/prey
@@ -24,10 +24,10 @@
if(!attack_same && L.faction != faction)
LAZYDISTINCTADD(prey, weakref(L))
-/datum/ai/beast
+/datum/mob_controller/beast
expected_type = /mob/living/simple_animal/hostile/retaliate/beast
-/datum/ai/beast/do_process(time_elapsed)
+/datum/mob_controller/beast/do_process(time_elapsed)
var/mob/living/simple_animal/hostile/retaliate/beast/beast = body
var/nut = beast.get_nutrition()
var/max_nut = beast.get_max_nutrition()
@@ -78,9 +78,11 @@
desc = "A fast, armoured predator accustomed to hiding and ambushing in cold terrain."
faction = "samak"
icon = 'icons/mob/simple_animal/samak.dmi'
- move_to_delay = 2
+ move_intents = list(
+ /decl/move_intent/walk/animal_fast,
+ /decl/move_intent/run/animal_fast
+ )
max_health = 125
- speed = 2
natural_weapon = /obj/item/natural_weapon/claws
cold_damage_per_tick = 0
speak_chance = 2.5
@@ -90,6 +92,7 @@
natural_armor = list(
ARMOR_MELEE = ARMOR_MELEE_KNIVES
)
+ base_movement_delay = 2
/mob/living/simple_animal/hostile/retaliate/beast/samak/alt
desc = "A fast, armoured predator accustomed to hiding and ambushing."
@@ -100,9 +103,11 @@
desc = "A small pack animal. Although omnivorous, it will hunt meat on occasion."
faction = "diyaab"
icon = 'icons/mob/simple_animal/diyaab.dmi'
- move_to_delay = 3
+ move_intents = list(
+ /decl/move_intent/walk/animal,
+ /decl/move_intent/run/animal
+ )
max_health = 25
- speed = 1
natural_weapon = /obj/item/natural_weapon/claws/weak
cold_damage_per_tick = 0
speak_chance = 2.5
@@ -110,15 +115,18 @@
emote_see = list("sniffs the air cautiously","looks around")
emote_hear = list("snuffles")
mob_size = MOB_SIZE_SMALL
+ base_movement_delay = 1
/mob/living/simple_animal/hostile/retaliate/beast/shantak
name = "shantak"
desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. Don't be fooled by its beauty though."
faction = "shantak"
icon = 'icons/mob/simple_animal/shantak.dmi'
- move_to_delay = 3
+ move_intents = list(
+ /decl/move_intent/walk/animal,
+ /decl/move_intent/run/animal
+ )
max_health = 75
- speed = 1
natural_weapon = /obj/item/natural_weapon/claws
cold_damage_per_tick = 0
speak_chance = 1
@@ -155,15 +163,18 @@
desc = "It looks like a crustacean with an exceedingly hard carapace. Watch the pinchers!"
faction = "crab"
icon = 'icons/mob/simple_animal/royalcrab.dmi'
- move_to_delay = 3
+ move_intents = list(
+ /decl/move_intent/walk/animal,
+ /decl/move_intent/run/animal
+ )
max_health = 150
- speed = 1
natural_weapon = /obj/item/natural_weapon/pincers
speak_chance = 0.5
emote_see = list("skitters","oozes liquid from its mouth", "scratches at the ground", "clicks its claws")
natural_armor = list(
ARMOR_MELEE = ARMOR_MELEE_RESISTANT
)
+ base_movement_delay = 1
/mob/living/simple_animal/hostile/retaliate/beast/charbaby
name = "charbaby"
@@ -172,14 +183,14 @@
mob_size = MOB_SIZE_LARGE
max_health = 45
natural_weapon = /obj/item/natural_weapon/charbaby
- speed = 2
return_damage_min = 2
return_damage_max = 3
harm_intent_damage = 1
blood_color = COLOR_NT_RED
natural_armor = list(
ARMOR_LASER = ARMOR_LASER_HANDGUNS
- )
+ )
+ base_movement_delay = 2
/obj/item/natural_weapon/charbaby
name = "scalding hide"
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/giant_crab.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_crab.dm
index 8f610870094..613be5056b9 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/giant_crab.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_crab.dm
@@ -7,7 +7,7 @@
emote_hear = list("clicks")
emote_see = list("clacks")
speak_chance = 0.5
- turns_per_move = 5
+ turns_per_wander = 5
butchery_data = /decl/butchery_data/animal/arthropod/crab/giant
can_escape = TRUE //snip snip
break_stuff_probability = 15
@@ -23,7 +23,7 @@
ARMOR_BULLET = ARMOR_BALLISTIC_PISTOL
)
ability_cooldown = 2 MINUTES
- ai = /datum/ai/giant_crab
+ ai = /datum/mob_controller/giant_crab
var/mob/living/human/victim //the human we're grabbing
var/grab_duration = 3 //duration of disable in life ticks to simulate a grab
@@ -31,10 +31,10 @@
var/list/grab_desc = list("thrashes", "squeezes", "crushes")
var/continue_grab_prob = 35 //probability that a successful grab will be extended by one life tick
-/datum/ai/giant_crab
+/datum/mob_controller/giant_crab
expected_type = /mob/living/simple_animal/hostile/retaliate/giant_crab
-/datum/ai/giant_crab/do_process(time_elapsed)
+/datum/mob_controller/giant_crab/do_process(time_elapsed)
. = ..()
var/mob/living/simple_animal/hostile/retaliate/giant_crab/crab = body
if((crab.current_health > crab.get_max_health() / 1.5) && length(crab.enemies) && prob(10))
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/jelly.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/jelly.dm
index 75c78bb3d30..70342ca411d 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/jelly.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/jelly.dm
@@ -3,12 +3,15 @@
desc = "It looks like a floating jellyfish. How does it do that?"
faction = "zeq"
icon = 'icons/mob/simple_animal/jelly.dmi'
- move_to_delay = 2
+ move_intents = list(
+ /decl/move_intent/walk/animal_fast,
+ /decl/move_intent/run/animal_fast
+ )
max_health = 75
- speed = 1
natural_weapon = /obj/item/natural_weapon/tentacles
speak_chance = 0.5
emote_see = list("wobbles slightly","oozes something out of tentacles' ends")
+ base_movement_delay = 1
var/gets_random_color = TRUE
/obj/item/natural_weapon/tentacles
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm
index e18e9eb4479..d18d66c3342 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/king_of_goats.dm
@@ -16,7 +16,10 @@
mob_size = MOB_SIZE_LARGE
mob_bump_flag = HEAVY
can_escape = TRUE
- move_to_delay = 3
+ move_intents = list(
+ /decl/move_intent/walk/animal,
+ /decl/move_intent/run/animal
+ )
min_gas = null
max_gas = null
minbodytemp = 0
@@ -105,7 +108,10 @@
icon = 'icons/mob/simple_animal/goat_master.dmi'
max_health = 200
natural_weapon = /obj/item/natural_weapon/goathorns
- move_to_delay = 3
+ move_intents = list(
+ /decl/move_intent/walk/animal,
+ /decl/move_intent/run/animal
+ )
/mob/living/simple_animal/hostile/retaliate/goat/king/Retaliate()
..()
@@ -116,12 +122,7 @@
set waitfor = FALSE
..()
if(spellscast < 5)
- if(prob(5) && move_to_delay != 1) //speed buff
- spellscast++
- visible_message(SPAN_MFAUNA("\The [src] shimmers and seems to phase in and out of reality itself!"))
- move_to_delay = 1
-
- else if(prob(5)) //stun move
+ if(prob(5)) //stun move
spellscast++
visible_message(SPAN_MFAUNA("\The [src]' fleece flashes with blinding light!"))
new /obj/item/grenade/flashbang/instant(src.loc)
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm
index 73c0c426397..4a9ed00e327 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm
@@ -37,16 +37,15 @@
emote_see = list("flutters its wings")
natural_weapon = /obj/item/natural_weapon/beak
speak_chance = 1 // 1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s
- turns_per_move = 5
+ turns_per_wander = 5
response_harm = "swats"
- stop_automated_movement = 1
+ stop_wandering = TRUE
universal_speak = TRUE
butchery_data = /decl/butchery_data/animal/bird/parrot
var/parrot_state = PARROT_WANDER // Hunt for a perch when created
var/parrot_sleep_max = 25 // The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in life() being run every single tick.
var/parrot_sleep_dur = 25 // Same as above, this is the var that physically counts down
- var/parrot_speed = 5 // Movement delay in ticks. Higher number = slower.
var/parrot_been_shot = 0 // Parrots get a speed bonus after being shot. This will deincrement every Life() and at 0 the parrot will return to regular speed.
//The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from,
//mobs it wants to attack or mobs that have attacked it
@@ -123,7 +122,7 @@
if(parrot_state == PARROT_PERCH)
parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
parrot_interest = user
- parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction..
+ parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction...
if(isliving(user))
var/mob/living/M = user
if(M.current_health < 50) //Weakened mob? Fight back!
@@ -198,7 +197,7 @@
//-----WANDERING - This is basically a 'I dont know what to do yet' state
else if(parrot_state == PARROT_WANDER)
//Stop movement, we'll set it later
- walk(src, 0)
+ stop_automove()
parrot_interest = null
//Wander around aimlessly. This will help keep the loops from searches down
@@ -207,7 +206,7 @@
SelfMove(pick(global.cardinal))
return
- if(!held_item && !parrot_perch) //If we've got nothing to do.. look for something to do.
+ if(!held_item && !parrot_perch) //If we've got nothing to do, look for something to do.
var/atom/movable/AM = search_for_perch_and_item() //This handles checking through lists so we know it's either a perch or stealable item
if(AM)
if((isitem(AM) && can_pick_up(AM)) || isliving(AM)) //If stealable item
@@ -236,7 +235,7 @@
return
//-----STEALING
else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL))
- walk(src,0)
+ stop_automove()
if(!parrot_interest || held_item)
parrot_state = PARROT_SWOOP | PARROT_RETURN
return
@@ -260,12 +259,13 @@
parrot_state = PARROT_SWOOP | PARROT_RETURN
return
- walk_to(src, parrot_interest, 1, parrot_speed)
+ set_moving_slowly()
+ start_automove(parrot_interest)
return
//-----RETURNING TO PERCH
else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN))
- walk(src, 0)
+ stop_automove()
if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else.
parrot_perch = null
parrot_state = PARROT_WANDER
@@ -278,17 +278,24 @@
update_icon()
return
- walk_to(src, parrot_perch, 1, parrot_speed)
+ set_moving_slowly()
+ start_automove(parrot_perch)
return
//-----FLEEING
else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE))
- walk(src,0)
+ stop_automove()
give_up()
if(!parrot_interest || !isliving(parrot_interest)) //Sanity
parrot_state = PARROT_WANDER
- walk_away(src, parrot_interest, 1, parrot_speed-parrot_been_shot)
+ var/static/datum/automove_metadata/_parrot_flee_automove_metadata = new(
+ _move_delay = 2,
+ _acceptable_distance = 7,
+ _avoid_target = TRUE
+ )
+ set_moving_quickly()
+ start_automove(parrot_interest, metadata = _parrot_flee_automove_metadata)
parrot_been_shot--
return
@@ -325,12 +332,13 @@
//Otherwise, fly towards the mob!
else
- walk_to(src, parrot_interest, 1, parrot_speed)
+ set_moving_quickly()
+ start_automove(parrot_interest)
return
//-----STATE MISHAP
else //This should not happen. If it does lets reset everything and try again
- walk(src,0)
+ stop_automove()
parrot_interest = null
parrot_perch = null
drop_held_item()
diff --git a/code/modules/mob/living/simple_animal/hostile/slug.dm b/code/modules/mob/living/simple_animal/hostile/slug.dm
index e4f6eafafb5..ac4e93dd497 100644
--- a/code/modules/mob/living/simple_animal/hostile/slug.dm
+++ b/code/modules/mob/living/simple_animal/hostile/slug.dm
@@ -6,8 +6,10 @@
response_harm = "stomps on"
destroy_surroundings = 0
max_health = 15
- speed = 0
- move_to_delay = 0
+ move_intents = list(
+ /decl/move_intent/walk/animal_fast,
+ /decl/move_intent/run/animal_fast
+ )
density = TRUE
min_gas = null
mob_size = MOB_SIZE_MINISCULE
@@ -16,6 +18,7 @@
natural_weapon = /obj/item/natural_weapon/bite
holder_type = /obj/item/holder/slug
faction = "Hostile Fauna"
+ base_movement_delay = 0
/mob/living/simple_animal/hostile/slug/proc/check_friendly_species(var/mob/living/M)
return istype(M) && M.faction == faction
diff --git a/code/modules/mob/living/simple_animal/hostile/tree.dm b/code/modules/mob/living/simple_animal/hostile/tree.dm
index 57c18b2f3b6..ecddb081881 100644
--- a/code/modules/mob/living/simple_animal/hostile/tree.dm
+++ b/code/modules/mob/living/simple_animal/hostile/tree.dm
@@ -3,13 +3,13 @@
desc = "A pissed off tree-like alien. It seems annoyed with the festivities..."
icon = 'icons/mob/simple_animal/pinetree.dmi'
speak_chance = 0
- turns_per_move = 5
+ turns_per_wander = 5
butchery_data = null
- speed = -1
max_health = 250
pixel_x = -16
harm_intent_damage = 5
natural_weapon = /obj/item/natural_weapon/bite
+ base_movement_delay = -1
//Space carp aren't affected by atmos.
min_gas = null
diff --git a/code/modules/mob/living/simple_animal/hostile/vagrant.dm b/code/modules/mob/living/simple_animal/hostile/vagrant.dm
index 50fb0091f5d..bbff171e105 100644
--- a/code/modules/mob/living/simple_animal/hostile/vagrant.dm
+++ b/code/modules/mob/living/simple_animal/hostile/vagrant.dm
@@ -4,10 +4,12 @@
desc = "You get the feeling you should run."
icon = 'icons/mob/simple_animal/vagrant.dmi'
max_health = 60
- speed = 5
speak_chance = 0
- turns_per_move = 4
- move_to_delay = 4
+ turns_per_wander = 4
+ move_intents = list(
+ /decl/move_intent/walk/animal_fast,
+ /decl/move_intent/run/animal_fast
+ )
break_stuff_probability = 0
faction = "vagrant"
harm_intent_damage = 3
@@ -20,6 +22,7 @@
pass_flags = PASS_FLAG_TABLE
bleed_colour = "#aad9de"
nutrition = 100
+ base_movement_delay = 5
var/cloaked = 0
var/mob/living/human/gripping = null
@@ -34,7 +37,7 @@
. = ..()
if(isliving(Proj.firer) && (target_mob != Proj.firer) && current_health < oldhealth && !incapacitated(INCAPACITATION_KNOCKOUT)) //Respond to being shot at
target_mob = Proj.firer
- turns_per_move = 3
+ turns_per_wander = 3
MoveToTarget()
/mob/living/simple_animal/hostile/vagrant/death(gibbed)
@@ -60,8 +63,8 @@
else
gripping = null
- if(turns_per_move != initial(turns_per_move))
- turns_per_move = initial(turns_per_move)
+ if(turns_per_wander != initial(turns_per_wander))
+ turns_per_wander = initial(turns_per_wander)
if(stance == HOSTILE_STANCE_IDLE && !cloaked)
cloaked = 1
@@ -79,12 +82,12 @@
alpha = 75
set_light(0)
icon_state = initial(icon_state)
- move_to_delay = initial(move_to_delay)
+ set_moving_slowly()
else //It's fight time
alpha = 255
icon_state += "-glowing"
set_light(3, 0.2)
- move_to_delay = 2
+ set_moving_quickly()
/mob/living/simple_animal/hostile/vagrant/attack_target(mob/target)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/passive/_passive.dm b/code/modules/mob/living/simple_animal/passive/_passive.dm
index 6cc2e8245ad..c9d3cb99c48 100644
--- a/code/modules/mob/living/simple_animal/passive/_passive.dm
+++ b/code/modules/mob/living/simple_animal/passive/_passive.dm
@@ -1,28 +1,35 @@
-/datum/ai/passive
+var/global/datum/automove_metadata/_flee_automove_metadata = new(
+ _move_delay = 2,
+ _acceptable_distance = 7,
+ _avoid_target = TRUE
+)
+
+/datum/mob_controller/passive
expected_type = /mob/living/simple_animal
var/weakref/flee_target
var/turns_since_scan
-/datum/ai/passive/proc/update_targets()
+/datum/mob_controller/passive/proc/update_targets()
//see if we should stop fleeing
var/mob/living/simple_animal/critter = body
var/atom/flee_target_atom = flee_target?.resolve()
if(istype(flee_target_atom) && (flee_target_atom.loc in view(body)))
- if(body.MayMove())
- walk_away(body, flee_target_atom, 7, 2)
- critter.stop_automated_movement = TRUE
+ critter.set_moving_quickly()
+ critter.start_automove(flee_target_atom, metadata = global._flee_automove_metadata)
+ critter.stop_wandering = TRUE
else
flee_target = null
- critter.stop_automated_movement = FALSE
+ critter.set_moving_slowly()
+ critter.stop_wandering = FALSE
return !isnull(flee_target)
-/datum/ai/passive/do_process(time_elapsed)
+/datum/mob_controller/passive/do_process(time_elapsed)
..()
// Handle fleeing from aggressors.
turns_since_scan++
if (turns_since_scan > 5)
- walk_to(body, 0)
+ body.stop_automove()
turns_since_scan = 0
if(update_targets())
return
@@ -37,7 +44,7 @@
body.set_stat(CONSCIOUS)
critter.wander = TRUE
-/datum/ai/passive/proc/set_flee_target(atom/A)
+/datum/mob_controller/passive/proc/set_flee_target(atom/A)
if(A)
flee_target = weakref(A)
turns_since_scan = 5
@@ -45,9 +52,9 @@
/mob/living/simple_animal/passive
possession_candidate = TRUE
abstract_type = /mob/living/simple_animal/passive
- ai = /datum/ai/passive
+ ai = /datum/mob_controller/passive
speak_chance = 0.5
- turns_per_move = 5
+ turns_per_wander = 5
see_in_dark = 6
minbodytemp = 223
maxbodytemp = 323
@@ -55,31 +62,31 @@
/mob/living/simple_animal/passive/attackby(var/obj/item/O, var/mob/user)
. = ..()
if(O.force)
- var/datum/ai/passive/preyi = ai
+ var/datum/mob_controller/passive/preyi = ai
if(istype(preyi))
preyi.set_flee_target(user? user : loc)
/mob/living/simple_animal/passive/default_hurt_interaction(mob/user)
. = ..()
if(.)
- var/datum/ai/passive/preyi = ai
+ var/datum/mob_controller/passive/preyi = ai
if(istype(preyi))
preyi.set_flee_target(user)
/mob/living/simple_animal/passive/explosion_act()
. = ..()
- var/datum/ai/passive/preyi = ai
+ var/datum/mob_controller/passive/preyi = ai
if(istype(preyi))
preyi.set_flee_target(loc)
/mob/living/simple_animal/passive/bullet_act(var/obj/item/projectile/proj)
. = ..()
- var/datum/ai/passive/preyi = ai
+ var/datum/mob_controller/passive/preyi = ai
if(istype(preyi))
preyi.set_flee_target(isliving(proj.firer) ? proj.firer : loc)
/mob/living/simple_animal/passive/hitby(atom/movable/AM, var/datum/thrownthing/TT)
. = ..()
- var/datum/ai/passive/preyi = ai
+ var/datum/mob_controller/passive/preyi = ai
if(istype(preyi))
preyi.set_flee_target(TT.thrower || loc)
diff --git a/code/modules/mob/living/simple_animal/passive/deer.dm b/code/modules/mob/living/simple_animal/passive/deer.dm
index 044390456dd..b8903039385 100644
--- a/code/modules/mob/living/simple_animal/passive/deer.dm
+++ b/code/modules/mob/living/simple_animal/passive/deer.dm
@@ -8,7 +8,7 @@
emote_see = list("shakes its head", "stamps a hoof", "looks around quickly")
emote_speech = list("Ough!", "Ourgh!", "Mroough!", "Broough?")
speak_chance = 0.5
- turns_per_move = 5
+ turns_per_wander = 5
see_in_dark = 6
faction = "deer"
max_health = 60
diff --git a/code/modules/mob/living/simple_animal/passive/fox.dm b/code/modules/mob/living/simple_animal/passive/fox.dm
index ae9ee47f9fc..b1ad39d92ca 100644
--- a/code/modules/mob/living/simple_animal/passive/fox.dm
+++ b/code/modules/mob/living/simple_animal/passive/fox.dm
@@ -1,8 +1,8 @@
-/datum/ai/passive/fox
+/datum/mob_controller/passive/fox
var/weakref/hunt_target
var/next_hunt = 0
-/datum/ai/passive/fox/update_targets()
+/datum/mob_controller/passive/fox/update_targets()
// Fleeing takes precedence.
. = ..()
if(!. && !hunt_target && world.time >= next_hunt) // TODO: generalized nutrition process. && body.get_nutrition() < body.get_max_nutrition() * 0.5)
@@ -13,10 +13,10 @@
return . || !!hunt_target
-/datum/ai/passive/fox/proc/can_hunt(mob/living/victim)
+/datum/mob_controller/passive/fox/proc/can_hunt(mob/living/victim)
return !victim.isSynthetic() && (victim.stat == DEAD || victim.get_object_size() < body.get_object_size())
-/datum/ai/passive/fox/do_process(time_elapsed)
+/datum/mob_controller/passive/fox/do_process(time_elapsed)
..()
@@ -27,13 +27,14 @@
var/atom/hunt_target_atom = hunt_target?.resolve()
if(!isliving(hunt_target_atom) || QDELETED(hunt_target_atom) || !(hunt_target_atom in view(body)))
hunt_target = null
- critter.stop_automated_movement = FALSE
+ critter.stop_wandering = FALSE
return
// Find or pursue the target.
if(!body.Adjacent(hunt_target_atom))
- critter.stop_automated_movement = 1
- walk_to(body, hunt_target_atom, 0, 3)
+ critter.set_moving_quickly()
+ critter.stop_wandering = TRUE
+ body.start_automove(hunt_target_atom)
return
// Hunt/consume the target.
@@ -43,7 +44,8 @@
if(QDELETED(hunt_mob))
hunt_target = null
- critter.stop_automated_movement = FALSE
+ critter.set_moving_slowly()
+ critter.stop_wandering = FALSE
return
if(hunt_mob.stat != DEAD)
@@ -51,7 +53,7 @@
// Eat the mob.
hunt_target = null
- critter.stop_automated_movement = FALSE
+ critter.stop_wandering = FALSE
body.visible_message(SPAN_DANGER("\The [body] consumes the body of \the [hunt_mob]!"))
var/remains_type = hunt_mob.get_remains_type()
if(remains_type)
@@ -69,7 +71,7 @@
desc = "A cunning and graceful predatory mammal, known for its red fur and eerie screams."
icon = 'icons/mob/simple_animal/fox.dmi'
natural_weapon = /obj/item/natural_weapon/bite/weak
- ai = /datum/ai/passive/fox
+ ai = /datum/mob_controller/passive/fox
mob_size = MOB_SIZE_SMALL
emote_speech = list("Yip!","AIEE!","YIPE!")
speak_emote = list("yelps", "yips", "hisses", "screams")
diff --git a/code/modules/mob/living/simple_animal/passive/mouse.dm b/code/modules/mob/living/simple_animal/passive/mouse.dm
index 0c7ce6f9f01..4fb3c20b809 100644
--- a/code/modules/mob/living/simple_animal/passive/mouse.dm
+++ b/code/modules/mob/living/simple_animal/passive/mouse.dm
@@ -9,7 +9,7 @@
emote_see = list("runs in a circle", "shakes", "scritches at something")
pass_flags = PASS_FLAG_TABLE
speak_chance = 0.5
- turns_per_move = 5
+ turns_per_wander = 5
see_in_dark = 6
max_health = 5
response_harm = "stamps on"
@@ -26,7 +26,7 @@
base_animal_type = /mob/living/simple_animal/passive/mouse
butchery_data = /decl/butchery_data/animal/small/furred
- ai = /datum/ai/passive/mouse
+ ai = /datum/mob_controller/passive/mouse
var/body_color //brown, gray and white, leave blank for random
var/splatted = FALSE
@@ -37,10 +37,10 @@
/mob/living/simple_animal/passive/mouse/get_dexterity(var/silent)
return DEXTERITY_NONE // Mice are troll bait, give them no power.
-/datum/ai/passive/mouse
+/datum/mob_controller/passive/mouse
expected_type = /mob/living/simple_animal/passive/mouse
-/datum/ai/passive/mouse/do_process()
+/datum/mob_controller/passive/mouse/do_process()
..()
var/mob/living/simple_animal/passive/mouse/mouse = body
if(prob(mouse.speak_chance))
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index b2354fbd88b..1770d2166ef 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -1,3 +1,4 @@
+
/mob/living/simple_animal
name = "animal"
max_health = 20
@@ -11,6 +12,13 @@
icon_state = ICON_STATE_WORLD
buckle_pixel_shift = @"{'x':0,'y':0,'z':8}"
+ move_intents = list(
+ /decl/move_intent/walk/animal,
+ /decl/move_intent/run/animal
+ )
+
+ var/base_movement_delay = 4
+
var/can_have_rider = TRUE
var/max_rider_size = MOB_SIZE_SMALL
@@ -25,11 +33,12 @@
/// Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps
var/list/emote_see
- var/turns_per_move = 1
- var/turns_since_move = 0
- var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals.
+ /// Wandering tracking vars.
+ var/turns_per_wander = 1
+ var/turns_since_wander = 0
var/wander = TRUE // Does the mob wander around when idle?
- var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is grabbing it.
+ var/stop_wandering = FALSE //Use this to temporarely stop random movement or to if you write special movement code for animals.
+ var/stop_wandering_when_pulled = TRUE //When set to 1 this stops the animal from moving when someone is grabbing it.
//Interaction
var/response_help_1p = "You pet $TARGET$."
@@ -53,7 +62,6 @@
)
var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above
- var/speed = 0 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster
//LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly
var/obj/item/natural_weapon/natural_weapon
@@ -184,7 +192,7 @@ var/global/list/simplemob_icon_bitflag_cache = list()
if(can_bleed && bleed_ticks > 0)
handle_bleeding()
if(is_aquatic && !submerged())
- walk(src, 0)
+ stop_automove()
if(HAS_STATUS(src, STAT_PARA) <= 2) // gated to avoid redundant update_icon() calls.
SET_STATUS_MAX(src, STAT_PARA, 3)
update_icon()
@@ -231,15 +239,15 @@ var/global/list/simplemob_icon_bitflag_cache = list()
if(current_posture.prone)
if(!incapacitated())
set_posture(/decl/posture/standing)
- else if(!stop_automated_movement && !buckled_mob && wander && !anchored)
- if(isturf(src.loc) && !current_posture.prone) //This is so it only moves if it's not inside a closet, gentics machine, etc.
- turns_since_move++
- if(turns_since_move >= turns_per_move && (!(stop_automated_movement_when_pulled) || !LAZYLEN(grabbed_by))) //Some animals don't move when pulled
- var/direction = pick(global.cardinal)
- var/turf/move_to = get_step(loc, direction)
- if(turf_is_safe(move_to))
- SelfMove(direction)
- turns_since_move = 0
+ else if(!stop_wandering && !buckled_mob && wander && !anchored && isturf(src.loc) && !current_posture.prone) //This is so it only moves if it's not inside a closet, gentics machine, etc.
+ turns_since_wander++
+ if(turns_since_wander >= turns_per_wander && (!(stop_wandering_when_pulled) || !LAZYLEN(grabbed_by))) //Some animals don't move when pulled
+ set_moving_slowly()
+ var/direction = pick(global.cardinal)
+ var/turf/move_to = get_step(loc, direction)
+ if(turf_is_safe(move_to))
+ SelfMove(direction)
+ turns_since_wander = 0
//Speaking
if(prob(speak_chance))
@@ -293,6 +301,13 @@ var/global/list/simplemob_icon_bitflag_cache = list()
if(!atmos_suitable)
take_damage(unsuitable_atmos_damage)
+/mob/living/simple_animal/get_mob_temperature_threshold(threshold, bodypart)
+ if(threshold >= HEAT_LEVEL_1)
+ return maxbodytemp
+ if(threshold <= COLD_LEVEL_1)
+ return minbodytemp
+ return ..()
+
/mob/living/simple_animal/proc/escape(mob/living/M, obj/O)
O.unbuckle_mob(M)
visible_message(SPAN_DANGER("\The [M] escapes from \the [O]!"))
@@ -391,15 +406,10 @@ var/global/list/simplemob_icon_bitflag_cache = list()
adjustBleedTicks(damage)
/mob/living/simple_animal/get_movement_delay(var/travel_dir)
- var/tally = ..() //Incase I need to add stuff other than "speed" later
-
- tally += speed
- if(purge)//Purged creatures will move more slowly. The more time before their purge stops, the slower they'll move.
- if(tally <= 0)
- tally = 1
- tally *= purge
-
- return tally+get_config_value(/decl/config/num/movement_animal)
+ . = max(1, ..() + base_movement_delay + get_config_value(/decl/config/num/movement_animal))
+ //Purged creatures will move more slowly. The more time before their purge stops, the slower they'll move.
+ if(purge)
+ . *= purge
/mob/living/simple_animal/Stat()
. = ..()
@@ -449,15 +459,6 @@ var/global/list/simplemob_icon_bitflag_cache = list()
W.forceMove(get_turf(src))
return 1
-/mob/living/simple_animal/handle_fire()
- return
-/mob/living/simple_animal/update_fire()
- return
-/mob/living/simple_animal/IgniteMob()
- return
-/mob/living/simple_animal/ExtinguishMob()
- return
-
/mob/living/simple_animal/is_burnable()
return heat_damage_per_tick
@@ -539,7 +540,7 @@ var/global/list/simplemob_icon_bitflag_cache = list()
return
for(var/gas in level_data.exterior_atmosphere.gas)
- var/gas_amt = level_data.exterior_atmosphere[gas]
+ var/gas_amt = level_data.exterior_atmosphere.gas[gas]
if(min_gas)
min_gas[gas] = round(gas_amt * 0.5)
if(max_gas)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index e236a36b671..15c71e2c496 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1,4 +1,7 @@
/mob/Destroy() //This makes sure that mobs with clients/keys are not just deleted from the game.
+
+ stop_automove()
+
STOP_PROCESSING(SSmobs, src)
global.dead_mob_list_ -= src
global.living_mob_list_ -= src
@@ -60,9 +63,9 @@
/mob/Initialize()
if(ispath(skillset))
skillset = new skillset(src)
- if(!move_intent)
+ if(!ispath(move_intent) || !(move_intent in move_intents))
move_intent = move_intents[1]
- if(ispath(move_intent))
+ if(!istype(move_intent))
move_intent = GET_DECL(move_intent)
. = ..()
ability_master = new(null, src)
@@ -216,7 +219,13 @@
. += 6
if(current_posture.prone) //Crawling, it's slower
. += (8 + ((GET_STATUS(src, STAT_WEAK) * 3) + (GET_STATUS(src, STAT_CONFUSE) * 2)))
- . += move_intent.move_delay + (ENCUMBERANCE_MOVEMENT_MOD * encumbrance())
+ var/_automove_delay = get_automove_delay()
+ if(isnull(_automove_delay))
+ . += move_intent.move_delay
+ else
+ . += _automove_delay
+ . = max(. + (ENCUMBERANCE_MOVEMENT_MOD * encumbrance()), 1)
+
#undef ENCUMBERANCE_MOVEMENT_MOD
/mob/proc/encumbrance()
@@ -1358,3 +1367,7 @@
var/decl/butchery_data/butchery_decl = GET_DECL(butchery_data)
. = butchery_decl?.meat_name || name
+/mob/reset_movement_delay()
+ var/datum/movement_handler/mob/delay/delay = locate() in movement_handlers
+ if(istype(delay))
+ delay.next_move = world.time
diff --git a/code/modules/mob/mob_automove.dm b/code/modules/mob/mob_automove.dm
new file mode 100644
index 00000000000..ff8d1a6d7ba
--- /dev/null
+++ b/code/modules/mob/mob_automove.dm
@@ -0,0 +1,24 @@
+/mob
+ /// Simple general-use reference for mob automoves. May be unused or replaced on subtypes.
+ var/atom/_automove_target
+
+/mob/stop_automove()
+ _automove_target = null
+ return ..()
+
+/// Called by get_movement_delay() to override the current move intent, in cases where an automove has a delay override.
+/mob/proc/get_automove_delay()
+ var/datum/automove_metadata/metadata = SSautomove.moving_metadata[src]
+ return metadata?.move_delay
+
+/mob/start_automove(target, movement_type, datum/automove_metadata/metadata)
+ _automove_target = target
+ return ..()
+
+// The AI datum may decide to track a target instead of using the mob reference.
+/mob/get_automove_target(datum/automove_metadata/metadata)
+ . = (istype(ai) && ai.get_automove_target()) || _automove_target || ..()
+
+// We do some early checking here to avoid doing the same checks repeatedly by calling SelfMove().
+/mob/can_do_automated_move(variant_move_delay)
+ . = MayMove() && !incapacitated() && (!istype(ai) || ai.can_do_automated_move())
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 9d750d10e17..57bd60dde48 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -148,7 +148,7 @@
var/list/progressbars = null //for stacking do_after bars
- var/datum/ai/ai // Type abused. Define with path and will automagically create. Determines behaviour for clientless mobs.
+ var/datum/mob_controller/ai // Type abused. Define with path and will automagically create. Determines behaviour for clientless mobs.
var/holder_type
/// If this mob is or was piloted by a player with typing indicators enabled, an instance of one.
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 174f36eaf06..78df63f89f2 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -1,9 +1,9 @@
/mob
var/moving = FALSE
-/mob/proc/SelfMove(var/direction)
+/atom/movable/proc/SelfMove(var/direction)
if(DoMove(direction, src) & MOVEMENT_HANDLED)
- return TRUE // Doesn't necessarily mean the mob physically moved
+ return TRUE // Doesn't necessarily mean the atom physically moved
/mob/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
. = current_posture.prone || ..() || !mover.density
@@ -313,7 +313,7 @@
var/choice = input(usr, "Select a default walk.", "Set Default Walk") as null|anything in get_movement_datums_by_missing_flag(MOVE_INTENT_QUICK)
if(choice && (choice in get_movement_datums_by_missing_flag(MOVE_INTENT_QUICK)))
default_walk_intent = choice
- to_chat(src, "You will now default to [default_walk_intent] when moving deliberately.")
+ to_chat(src, SPAN_NOTICE("You will now default to [default_walk_intent] when moving deliberately."))
/mob/verb/SetDefaultRun()
set name = "Set Default Run"
@@ -322,7 +322,7 @@
var/choice = input(usr, "Select a default run.", "Set Default Run") as null|anything in get_movement_datums_by_flag(MOVE_INTENT_QUICK)
if(choice && (choice in get_movement_datums_by_flag(MOVE_INTENT_QUICK)))
default_run_intent = choice
- to_chat(src, "You will now default to [default_run_intent] when moving quickly.")
+ to_chat(src, SPAN_NOTICE("You will now default to [default_run_intent] when moving quickly."))
/client/verb/setmovingslowly()
set hidden = 1
@@ -347,7 +347,7 @@
set_move_intent(default_run_intent)
/mob/proc/can_sprint()
- return FALSE
+ return TRUE
/mob/proc/adjust_stamina(var/amt)
return
diff --git a/code/modules/mob/observer/eye/freelook/read_me.dm b/code/modules/mob/observer/eye/freelook/read_me.dm
index fe7edf80295..76338342416 100644
--- a/code/modules/mob/observer/eye/freelook/read_me.dm
+++ b/code/modules/mob/observer/eye/freelook/read_me.dm
@@ -13,7 +13,7 @@
With this, the AI controls an "AI Eye" mob, which moves just like a ghost; such as moving through walls and being invisible to players.
The AI's eye is set to this mob and then we use a system (explained below) to determine what the cameras around the AI Eye can and
cannot see. If the camera cannot see a turf, it will black it out, otherwise it won't and the AI will be able to see it.
- This creates several features, such as.. no more see-through-wall cameras, easier to control camera movement, easier tracking,
+ This creates several features, such as: no more see-through-wall cameras, easier to control camera movement, easier tracking,
the AI only being able to track mobs which are visible to a camera, only trackable mobs appearing on the mob list and many more.
diff --git a/code/modules/modular_computers/computers/subtypes/dev_holo.dm b/code/modules/modular_computers/computers/subtypes/dev_holo.dm
index d74e6d13fee..f2460d4efdf 100644
--- a/code/modules/modular_computers/computers/subtypes/dev_holo.dm
+++ b/code/modules/modular_computers/computers/subtypes/dev_holo.dm
@@ -75,7 +75,7 @@
update_lighting()
-// Subtypes. It's not exactly.. well, presets, so i'll put it here for now.
+// Subtypes. It's not exactly... well, presets, so i'll put it here for now.
/obj/item/modular_computer/holotablet/round
name = "round holotablet"
diff --git a/code/modules/modular_computers/file_system/programs/generic/game.dm b/code/modules/modular_computers/file_system/programs/generic/game.dm
index 77c40f0223e..c5c9b2fdd3d 100644
--- a/code/modules/modular_computers/file_system/programs/generic/game.dm
+++ b/code/modules/modular_computers/file_system/programs/generic/game.dm
@@ -7,7 +7,7 @@
filedesc = "Unknown Game" // User-Friendly name. In this case, we will generate a random name in constructor.
program_icon_state = "game" // Icon state of this program's screen.
program_menu_icon = "script"
- extended_desc = "Fun for the whole family! Probably not an AAA title, but at least you can download it on the corporate network.." // A nice description.
+ extended_desc = "Fun for the whole family! Probably not an AAA title, but at least you can download it on the corporate network." // A nice description.
size = 5 // Size in GQ. Integers only. Smaller sizes should be used for utility/low use programs (like this one), while large sizes are for important programs.
available_on_network = 1 // ... but we want it to be available for download.
nanomodule_path = /datum/nano_module/arcade_classic/ // Path of relevant nano module. The nano module is defined further in the file.
diff --git a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm
index 525f363d285..563791757be 100644
--- a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm
+++ b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm
@@ -135,7 +135,7 @@
// This IF cuts on data transferred to client, so i guess it's worth it.
if(prog.downloaderror) // Download errored. Wait until user resets the program.
data["error"] = prog.downloaderror
- if(prog.current_transfer) // Download running. Wait please..
+ if(prog.current_transfer) // Download running. Wait please...
data |= prog.current_transfer.get_ui_data()
data["downloadspeed"] = prog.current_transfer.get_transfer_speed()
var/datum/computer_file/program/P = prog.current_transfer.transferring
diff --git a/code/modules/modular_computers/os/ui.dm b/code/modules/modular_computers/os/ui.dm
index 0b6529519df..59ec11f651e 100644
--- a/code/modules/modular_computers/os/ui.dm
+++ b/code/modules/modular_computers/os/ui.dm
@@ -177,7 +177,7 @@
var/obj/item/cell/battery = battery_module?.get_cell()
if(battery)
switch(battery.percent())
- if(80 to 200) // 100 should be maximal but just in case..
+ if(80 to 200) // 100 should be maximal but just in case...
data["PC_batteryicon"] = "batt_100.gif"
if(60 to 80)
data["PC_batteryicon"] = "batt_80.gif"
diff --git a/code/modules/multiz/level_data.dm b/code/modules/multiz/level_data.dm
index 9843da3b8f6..343808267b8 100644
--- a/code/modules/multiz/level_data.dm
+++ b/code/modules/multiz/level_data.dm
@@ -190,7 +190,7 @@
///Handle copying data from a previous level_data we're replacing.
/datum/level_data/proc/copy_from(var/datum/level_data/old_level)
- //#TODO: It's not really clear what should get moved over by default. But putting some time to reflect on this would be good..
+ //#TODO: It's not really clear what should get moved over by default. But putting some time to reflect on this would be good...
return
///Initialize the turfs on the z-level.
diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm
index 3721d55e684..d050a63716d 100644
--- a/code/modules/organs/internal/brain.dm
+++ b/code/modules/organs/internal/brain.dm
@@ -68,7 +68,7 @@
if(istype(_brainmob) && _brainmob?.client) //if thar be a brain inside... the brain.
to_chat(user, "You can feel the small spark of life still left in this one.")
else
- to_chat(user, "This one seems particularly lifeless. Perhaps it will regain some of its luster later..")
+ to_chat(user, "This one seems particularly lifeless. Perhaps it will regain some of its luster later.")
/obj/item/organ/internal/brain/do_install(mob/living/target, affected, in_place, update_icon, detached)
if(!(. = ..()))
diff --git a/code/modules/overmap/ships/computers/engine_control.dm b/code/modules/overmap/ships/computers/engine_control.dm
index 6aa0a035a86..a62b7258705 100644
--- a/code/modules/overmap/ships/computers/engine_control.dm
+++ b/code/modules/overmap/ships/computers/engine_control.dm
@@ -61,7 +61,7 @@
return TOPIC_REFRESH
if(href_list["set_global_limit"])
- var/newlim = input("Input new thrust limit (0..100%)", "Thrust limit", linked.get_thrust_limit() * 100) as num
+ var/newlim = input("Input new thrust limit (0-100%)", "Thrust limit", linked.get_thrust_limit() * 100) as num
if(!CanInteract(user, state))
return TOPIC_NOACTION
var/thrust_limit = clamp(newlim / 100, 0, 1)
@@ -76,7 +76,7 @@
if(href_list["engine"])
if(href_list["set_limit"])
var/datum/extension/ship_engine/E = locate(href_list["engine"])
- var/newlim = input("Input new thrust limit (0..100)", "Thrust limit", E.thrust_limit) as num
+ var/newlim = input("Input new thrust limit (0-100)", "Thrust limit", E.thrust_limit) as num
if(!CanInteract(user, state))
return
var/limit = clamp(newlim/100, 0, 1)
diff --git a/code/modules/paperwork/adminpaper.dm b/code/modules/paperwork/adminpaper.dm
index 11891f5d55a..a7d1cda991a 100644
--- a/code/modules/paperwork/adminpaper.dm
+++ b/code/modules/paperwork/adminpaper.dm
@@ -112,7 +112,7 @@
if(href_list["confirm"])
var/obj/machinery/faxmachine/F = destination_ref.resolve()
if(!istype(F))
- to_chat(usr, "The destination machines doesn't exist anymore..")
+ to_chat(usr, "The destination machine doesn't exist anymore.")
return
switch(alert("Are you sure you want to send the fax as is?",, "Yes", "No"))
if("Yes")
diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm
index 1483c7f0436..210ef6465b3 100644
--- a/code/modules/paperwork/handlabeler.dm
+++ b/code/modules/paperwork/handlabeler.dm
@@ -45,7 +45,7 @@
if(length(label))
to_chat(user, "Its label text reads '[SPAN_ITALIC(label)]'.")
else
- to_chat(user, SPAN_NOTICE("You're too far away to tell much more.."))
+ to_chat(user, SPAN_NOTICE("You're too far away to tell much more."))
/obj/item/hand_labeler/use_on_mob(mob/living/target, mob/living/user, animate = TRUE)
return FALSE
@@ -91,7 +91,7 @@
update_icon()
/obj/item/hand_labeler/proc/show_action_radial_menu(var/mob/user)
- //#TODO: Cache some of that stuff..
+ //#TODO: Cache some of that stuff.
var/image/btn_power = image('icons/screen/radial.dmi', icon_state = safety? "radial_power" : "radial_power_off")
btn_power.plane = FLOAT_PLANE
btn_power.layer = FLOAT_LAYER
diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm
index 11e8c049dd4..90273a9a956 100644
--- a/code/modules/paperwork/paper_bundle.dm
+++ b/code/modules/paperwork/paper_bundle.dm
@@ -424,7 +424,7 @@
/obj/item/paper_bundle/DefaultTopicState()
return global.paper_topic_state
-//We don't contain any matter, since we're not really a material thing..
+//We don't contain any matter, since we're not really a material thing.
/obj/item/paper_bundle/create_matter()
UNSETEMPTY(matter)
diff --git a/code/modules/paperwork/papershredder.dm b/code/modules/paperwork/papershredder.dm
index da22ac08272..87e33c56b27 100644
--- a/code/modules/paperwork/papershredder.dm
+++ b/code/modules/paperwork/papershredder.dm
@@ -112,15 +112,16 @@
return
return TRUE
-/obj/machinery/papershredder/attackby(var/obj/item/W, var/mob/user)
- if(!has_extension(W, /datum/extension/tool)) //Silently skip tools
- var/trying_to_smack = !(W.item_flags & ITEM_FLAG_NO_BLUDGEON) && user && user.a_intent == I_HURT
- if(W.storage)
- empty_bin(user, W)
+/obj/machinery/papershredder/attackby(var/obj/item/used_item, var/mob/user)
+ //Silently skip tools, and things we don't have the dexterity to use
+ if(!has_extension(used_item, /datum/extension/tool) && used_item.user_can_wield(user, silent = TRUE))
+ var/trying_to_smack = !(used_item.item_flags & ITEM_FLAG_NO_BLUDGEON) && user && user.a_intent == I_HURT
+ if(used_item.storage)
+ empty_bin(user, used_item)
return TRUE
- else if(!trying_to_smack && can_shred(W))
- shred(W, user)
+ else if(!trying_to_smack && can_shred(used_item))
+ shred(used_item, user)
return TRUE
return ..()
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index a7756f7e6d0..6ed25f22696 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -443,20 +443,24 @@ var/global/list/all_apcs = list()
return wires.Interact(user)
return ..()
-/obj/machinery/power/apc/bash(obj/item/W, mob/user)
- if (!(user.a_intent == I_HURT) || (W.item_flags & ITEM_FLAG_NO_BLUDGEON))
+/obj/machinery/power/apc/bash(obj/item/used_item, mob/user)
+ if (!(user.a_intent == I_HURT) || (used_item.item_flags & ITEM_FLAG_NO_BLUDGEON))
return
- if(!panel_open && W.force >= 5 && W.w_class >= ITEM_SIZE_NORMAL)
+ if(!used_item.user_can_wield(user))
+ return FALSE
+
+ . = ..()
+ if(. && !panel_open && used_item.w_class >= ITEM_SIZE_NORMAL)
if (((stat & BROKEN) || (hacker && !hacker.hacked_apcs_hidden)) && prob(20))
- playsound(get_turf(src), 'sound/weapons/smash.ogg', 75, 1)
+ playsound(get_turf(src), 'sound/weapons/smash.ogg', 75, TRUE)
if(force_open_panel(user) == MCS_CHANGE)
cover_removed = TRUE
- user.visible_message("The APC cover was knocked down with the [W.name] by [user.name]!", \
- "You knock down the APC cover with your [W.name]!", \
- "You hear a bang.")
- return TRUE
- return ..()
+ user.visible_message(
+ SPAN_DANGER("\The [user] knocks open the APC cover with \the [used_item]!"),
+ SPAN_DANGER("You knock down the APC cover with your [used_item.name]!"),
+ "You hear a bang."
+ )
// attack with hand - remove cell (if cover open) or interact with the APC
diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm
index c1e1c7610d1..bf0c3c7fc75 100644
--- a/code/modules/power/breaker_box.dm
+++ b/code/modules/power/breaker_box.dm
@@ -46,18 +46,18 @@
/obj/machinery/power/breakerbox/attack_ai(mob/living/silicon/ai/user)
if(update_locked)
- to_chat(user, "System locked. Please try again later.")
+ to_chat(user, SPAN_WARNING("System locked. Please try again later."))
return
if(busy)
- to_chat(user, "System is busy. Please wait until current operation is finished before changing power settings.")
+ to_chat(user, SPAN_WARNING("System is busy. Please wait until current operation is finished before changing power settings."))
return
busy = 1
- to_chat(user, "Updating power settings..")
+ to_chat(user, SPAN_GOOD("Updating power settings..."))
if(do_after(user, 50, src))
set_state(!on)
- to_chat(user, "Update Completed. New setting:[on ? "on": "off"]")
+ to_chat(user, SPAN_GOOD("Update completed. New setting:[on ? "on": "off"]"))
update_locked = 1
spawn(600)
update_locked = 0
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index 9a6452e04af..4fbd6dea467 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -1,4 +1,4 @@
-// It.. uses a lot of power. Everything under power is engineering stuff, at least.
+// It... uses a lot of power. Everything under power is engineering stuff, at least.
/obj/machinery/computer/gravity_control_computer
name = "Gravity Generator Control"
diff --git a/code/modules/power/smes_construction.dm b/code/modules/power/smes_construction.dm
index e685cc0e8dc..34211b1049e 100644
--- a/code/modules/power/smes_construction.dm
+++ b/code/modules/power/smes_construction.dm
@@ -207,7 +207,7 @@
// Sparks, Near - instantkill shock, Strong EMP, 25% light overload, 5% APC failure. 50% of SMES explosion. This is bad.
spark_at(src, amount = 10, cardinal_only = TRUE)
to_chat(h_user, SPAN_WARNING("Massive electrical arc sparks between you and [src].
Last thing you can think about is \"Oh shit...\""))
- // Remember, we have few gigajoules of electricity here.. Turn them into crispy toast.
+ // Remember, we have few gigajoules of electricity here. Turn them into crispy toast.
h_user.electrocute_act(rand(170,210), src, def_zone = ran_zone(null))
SET_STATUS_MAX(h_user, STAT_PARA, 8)
spawn(0)
@@ -230,7 +230,7 @@
return
src.ping("DANGER! Magnetic containment field failure in 3 ... 2 ... 1 ...")
explosion(src.loc,1,2,4,8)
- // Not sure if this is necessary, but just in case the SMES *somehow* survived..
+ // Not sure if this is necessary, but just in case the SMES *somehow* survived.
qdel(src)
/obj/machinery/power/smes/buildable/proc/check_total_system_failure(var/mob/user)
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 6f18ee8cdcc..cfbf0a78c32 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -649,6 +649,8 @@
. = 1
/obj/item/gun/attack_self(mob/user)
+ if(!user.check_dexterity(DEXTERITY_WEAPONS))
+ return TRUE // prevent further interactions
var/datum/firemode/new_mode = switch_firemodes()
if(prob(20) && !user.skill_check(SKILL_WEAPONS, SKILL_BASIC))
new_mode = switch_firemodes()
@@ -656,6 +658,8 @@
to_chat(user, "\The [src] is now set to [new_mode.name].")
/obj/item/gun/proc/toggle_safety(var/mob/user)
+ if(user && !user.check_dexterity(DEXTERITY_WEAPONS))
+ return TRUE
if(!has_safety)
to_chat(user,SPAN_NOTICE("You can't find a safety on \the [src]!"))
return
@@ -741,6 +745,11 @@
name = "Toggle Gun Safety"
expected_target_type = /obj/item/gun
+/decl/interaction_handler/toggle_safety/is_possible(atom/target, mob/user, obj/item/prop)
+ . = ..()
+ if(!user.check_dexterity(DEXTERITY_WEAPONS))
+ return FALSE
+
/decl/interaction_handler/toggle_safety/invoked(atom/target, mob/user, obj/item/prop)
var/obj/item/gun/gun = target
gun.toggle_safety(user)
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index e39dd188ecf..a331caa65fe 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -150,11 +150,11 @@
if(M)
M.welding_eyecheck()//Welding tool eye check
if(check_accidents(M, "[M] loses grip on [src] from its sudden recoil!",SKILL_CONSTRUCTION, 60, SKILL_ADEPT))
- return 0
+ return FALSE
spark_at(src, amount = 5, holder = src)
- return 1
+ return TRUE
handle_click_empty(M)
- return 0
+ return FALSE
/obj/item/gun/energy/plasmacutter/is_special_cutting_tool(var/high_power)
return TRUE
diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm
index f91b9fc9d49..ebfa76b2031 100644
--- a/code/modules/projectiles/guns/projectile/shotgun.dm
+++ b/code/modules/projectiles/guns/projectile/shotgun.dm
@@ -30,6 +30,8 @@
return null
/obj/item/gun/projectile/shotgun/pump/attack_self(mob/user)
+ if(!user_can_wield(user))
+ return TRUE
if(world.time >= recentpump + 10)
pump(user)
recentpump = world.time
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 415933456b6..b0f57d7741c 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -156,7 +156,9 @@
def_zone = check_zone(target_zone)
firer = shooter
var/direct_target
- if(get_turf(target) == get_turf(src))
+ var/turf/actual_target_turf = get_turf(target)
+ actual_target_turf = actual_target_turf?.resolve_to_actual_turf()
+ if(actual_target_turf == get_turf(src))
direct_target = target
preparePixelProjectile(target, shooter ? shooter : get_turf(src), params, forced_spread)
return fire(Angle_override, direct_target)
@@ -383,7 +385,7 @@
qdel(src)
return
var/turf/target = locate(clamp(starting + xo, 1, world.maxx), clamp(starting + yo, 1, world.maxy), starting.z)
- setAngle(get_projectile_angle(src, target))
+ setAngle(get_projectile_angle(src, target.resolve_to_actual_turf()))
if(dispersion)
var/DeviationAngle = (dispersion * 15)
setAngle(Angle + rand(-DeviationAngle, DeviationAngle))
@@ -417,6 +419,7 @@
/obj/item/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, Angle_offset = 0)
var/turf/curloc = get_turf(source)
var/turf/targloc = get_turf(target)
+ targloc = targloc?.resolve_to_actual_turf()
forceMove(get_turf(source))
starting = get_turf(source)
original = target
@@ -495,7 +498,7 @@
//Returns true if the target atom is on our current turf and above the right layer
/obj/item/projectile/proc/can_hit_target(atom/target, var/list/passthrough)
- return (target && ((target.layer >= TURF_LAYER + 0.3) || ismob(target)) && (loc == get_turf(target)) && (!(target in passthrough)))
+ return (target && ((target.layer >= STRUCTURE_LAYER) || ismob(target)) && (loc == get_turf(target)) && (!(target in passthrough)))
/proc/calculate_projectile_Angle_and_pixel_offsets(mob/user, params)
var/list/mouse_control = params2list(params)
diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm
index c1237db561e..779b990371c 100644
--- a/code/modules/reagents/reagent_containers.dm
+++ b/code/modules/reagents/reagent_containers.dm
@@ -97,20 +97,25 @@
/obj/item/chems/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
return
-/obj/item/chems/attackby(obj/item/W, mob/user)
- if(IS_PEN(W))
- var/tmp_label = sanitize_safe(input(user, "Enter a label for [name]", "Label", label_text), MAX_NAME_LEN)
- if(length(tmp_label) > 10)
- to_chat(user, SPAN_NOTICE("The label can be at most 10 characters long."))
- else
- to_chat(user, SPAN_NOTICE("You set the label to \"[tmp_label]\"."))
- label_text = tmp_label
- update_container_name()
- else
- return ..()
+/obj/item/chems/attackby(obj/item/used_item, mob/user)
+ if(used_item.user_can_wield(user, silent = TRUE))
+ if(IS_PEN(used_item))
+ var/tmp_label = sanitize_safe(input(user, "Enter a label for [name]", "Label", label_text), MAX_NAME_LEN)
+ if(length(tmp_label) > 10)
+ to_chat(user, SPAN_NOTICE("The label can be at most 10 characters long."))
+ else
+ to_chat(user, SPAN_NOTICE("You set the label to \"[tmp_label]\"."))
+ label_text = tmp_label
+ update_container_name()
+ return TRUE
+ return ..()
/obj/item/chems/standard_pour_into(mob/user, atom/target, amount = 5)
- return ..(user, target, amount_per_transfer_from_this)
+ amount = amount_per_transfer_from_this
+ // We'll be lenient: if you lack the dexterity for proper pouring you get a random amount.
+ if(!user_can_wield(user, silent = TRUE))
+ amount = rand(1, floor(amount_per_transfer_from_this * 1.5))
+ return ..(user, target, amount)
/obj/item/chems/do_surgery(mob/living/M, mob/living/user)
if(user.get_target_zone() != BP_MOUTH) //in case it is ever used as a surgery tool
diff --git a/code/modules/reagents/reagent_containers/food/burgers.dm b/code/modules/reagents/reagent_containers/food/burgers.dm
index d50cbe04186..e0abdb213ad 100644
--- a/code/modules/reagents/reagent_containers/food/burgers.dm
+++ b/code/modules/reagents/reagent_containers/food/burgers.dm
@@ -97,7 +97,7 @@
/obj/item/chems/food/tofuburger
name = "tofu burger"
- desc = "What.. is that meat?"
+ desc = "What... is that meat?"
icon_state = "tofuburger"
filling_color = "#fffee0"
center_of_mass = @'{"x":16,"y":10}'
@@ -190,7 +190,7 @@
/obj/item/chems/food/jellyburger
name = "jelly burger"
- desc = "Culinary delight..?"
+ desc = "Culinary delight...?"
icon_state = "jellyburger"
filling_color = "#b572ab"
center_of_mass = @'{"x":16,"y":11}'
diff --git a/code/modules/reagents/reagent_containers/food/rotten.dm b/code/modules/reagents/reagent_containers/food/rotten.dm
index 6cc5e6f98eb..7bd6d271426 100644
--- a/code/modules/reagents/reagent_containers/food/rotten.dm
+++ b/code/modules/reagents/reagent_containers/food/rotten.dm
@@ -42,7 +42,7 @@
/obj/item/chems/food/old/hotdog
name = "hotdog"
- desc = "This one is probably only marginally less safe to eat than when it was first created.."
+ desc = "This is probably only marginally less safe to eat than when it was first created."
icon_state = "ancient_hotdog"
/obj/item/chems/food/old/taco
diff --git a/code/modules/reagents/reagent_containers/food/soup.dm b/code/modules/reagents/reagent_containers/food/soup.dm
index 0a0853415ce..50e81182ab8 100644
--- a/code/modules/reagents/reagent_containers/food/soup.dm
+++ b/code/modules/reagents/reagent_containers/food/soup.dm
@@ -264,7 +264,7 @@
/obj/item/chems/food/beetsoup
name = "beet soup"
- desc = "Wait, how do you spell it again..?"
+ desc = "Wait, how do you spell it again...?"
icon_state = "beetsoup"
trash = /obj/item/trash/snack_bowl
filling_color = "#fac9ff"
diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm
index 5eb4506f3f8..196e754669f 100644
--- a/code/modules/shieldgen/emergency_shield.dm
+++ b/code/modules/shieldgen/emergency_shield.dm
@@ -95,7 +95,7 @@
//This seemed to be the best sound for hitting a force field.
playsound(src.loc, 'sound/effects/EMPulse.ogg', 100, 1)
check_failure()
- //The shield becomes dense to absorb the blow.. purely asthetic.
+ //The shield becomes dense to absorb the blow. Purely asthetic.
set_opacity(1)
spawn(20)
if(!QDELETED(src))
diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm
index fec5d27cde6..90220230fb6 100644
--- a/code/modules/species/species.dm
+++ b/code/modules/species/species.dm
@@ -198,7 +198,7 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200
var/manual_dexterity = DEXTERITY_FULL
- var/datum/ai/ai // Type abused. Define with path and will automagically create. Determines behaviour for clientless mobs. This will override mob AIs.
+ var/datum/mob_controller/ai // Type abused. Define with path and will automagically create. Determines behaviour for clientless mobs. This will override mob AIs.
var/exertion_emote_chance = 5
var/exertion_effect_chance = 0
diff --git a/code/modules/species/species_bodytype_helpers.dm b/code/modules/species/species_bodytype_helpers.dm
index 8736740277b..312b55d31f9 100644
--- a/code/modules/species/species_bodytype_helpers.dm
+++ b/code/modules/species/species_bodytype_helpers.dm
@@ -1,6 +1,9 @@
-/decl/bodytype/proc/get_ignited_icon(var/mob/living/human/H)
+/decl/bodytype/proc/get_ignited_icon(var/mob/living/human/victim)
return ignited_icon
+/decl/bodytype/proc/get_ignited_icon_state(mob/living/victim)
+ return "Standing"
+
/decl/bodytype/proc/get_icon_cache_uid(var/mob/H)
if(!icon_cache_uid)
icon_cache_uid = "[sequential_id(/decl/bodytype)]"
diff --git a/code/modules/species/species_bodytype_quadruped.dm b/code/modules/species/species_bodytype_quadruped.dm
index 5673e68d89d..93f1693fc34 100644
--- a/code/modules/species/species_bodytype_quadruped.dm
+++ b/code/modules/species/species_bodytype_quadruped.dm
@@ -22,4 +22,7 @@
/decl/bodytype/quadruped/apply_appearance(var/mob/living/human/H)
. = ..()
H.can_buckle = ridable
- H.buckle_pixel_shift = riding_offset
\ No newline at end of file
+ H.buckle_pixel_shift = riding_offset
+
+/decl/bodytype/quadruped/get_ignited_icon_state(mob/living/victim)
+ return "Generic_mob_burning"
\ No newline at end of file
diff --git a/code/modules/species/station/monkey.dm b/code/modules/species/station/monkey.dm
index 33decbf2245..4a68a4edaf3 100644
--- a/code/modules/species/station/monkey.dm
+++ b/code/modules/species/station/monkey.dm
@@ -38,4 +38,4 @@
TAG_FACTION = /decl/cultural_info/faction/other
)
- ai = /datum/ai/monkey
+ ai = /datum/mob_controller/monkey
diff --git a/code/modules/spells/aoe_turf/conjure/force_portal.dm b/code/modules/spells/aoe_turf/conjure/force_portal.dm
index b81c05f8020..293e176051f 100644
--- a/code/modules/spells/aoe_turf/conjure/force_portal.dm
+++ b/code/modules/spells/aoe_turf/conjure/force_portal.dm
@@ -1,6 +1,6 @@
/spell/aoe_turf/conjure/force_portal
name = "Force Portal"
- desc = "Create a portal that sucks in anything that touches it and then shoots it all out at the end.."
+ desc = "Create a portal that sucks in anything that touches it and then shoots it all out at the end."
school = "conjuration"
feedback = "FP"
summon_type = list(/obj/effect/force_portal)
diff --git a/code/modules/surgery/_surgery.dm b/code/modules/surgery/_surgery.dm
index 7ca2c764c28..11970dd7288 100644
--- a/code/modules/surgery/_surgery.dm
+++ b/code/modules/surgery/_surgery.dm
@@ -238,6 +238,10 @@ var/global/list/surgery_tool_exception_cache = list()
if(!zone)
return FALSE // Erroneous mob interaction
+ // there IS a rule that says dogs can't do surgery, actually. section 13a, the "No Dr. Air Bud" Rule
+ if(!user.check_dexterity(DEXTERITY_COMPLEX_TOOLS))
+ return TRUE // prevent other interactions because we've shown a message
+
var/decl/bodytype/root_bodytype = M.get_bodytype()
if(root_bodytype && length(LAZYACCESS(root_bodytype.limb_mapping, zone)) > 1)
zone = input("Which bodypart do you wish to operate on?", "Non-standard surgery") as null|anything in root_bodytype.limb_mapping[zone]
diff --git a/code/modules/synthesized_instruments/song.dm b/code/modules/synthesized_instruments/song.dm
index 6ec5112d138..77fef5ee6e2 100644
--- a/code/modules/synthesized_instruments/song.dm
+++ b/code/modules/synthesized_instruments/song.dm
@@ -48,7 +48,7 @@
var/note_num = delta1+delta2+global.musical_config.nn2no[note]
if (note_num < 0 || note_num > 127)
- CRASH("play_synthesized note failed because of 0..127 condition, [note], [acc], [oct]")
+ CRASH("play_synthesized note failed because of 0-127 condition, [note], [acc], [oct]")
var/datum/sample_pair/pair = src.instrument_data.sample_map[global.musical_config.n2t(note_num)]
#define Q 0.083 // 1/12
diff --git a/code/modules/tools/archetypes/tool_item.dm b/code/modules/tools/archetypes/tool_item.dm
index 0f3310a89a2..ae462efdd6e 100644
--- a/code/modules/tools/archetypes/tool_item.dm
+++ b/code/modules/tools/archetypes/tool_item.dm
@@ -46,6 +46,9 @@
if(get_tool_quality(archetype) <= 0)
return FALSE
+ if(!user_can_wield(user))
+ return FALSE
+
. = handle_tool_interaction(archetype, user, src, target, delay, start_message, success_message, failure_message, fuel_expenditure, check_skill, prefix_message, suffix_message, check_skill_threshold, check_skill_prob, set_cooldown)
if(QDELETED(user) || QDELETED(target))
diff --git a/code/modules/tools/subtypes/axes.dm b/code/modules/tools/subtypes/axes.dm
index 74911ae6ed3..1732441b3d0 100644
--- a/code/modules/tools/subtypes/axes.dm
+++ b/code/modules/tools/subtypes/axes.dm
@@ -21,6 +21,9 @@
)
return tool_qualities
+/obj/item/tool/axe/ebony
+ handle_material = /decl/material/solid/organic/wood/ebony
+
// Legacy SS13 hatchet.
/obj/item/tool/axe/hatchet
name = "hatchet"
diff --git a/code/modules/tools/subtypes/shovel.dm b/code/modules/tools/subtypes/shovel.dm
index a30282ea41f..177b5110221 100644
--- a/code/modules/tools/subtypes/shovel.dm
+++ b/code/modules/tools/subtypes/shovel.dm
@@ -22,6 +22,9 @@
/obj/item/tool/shovel/wood
material = /decl/material/solid/organic/wood
+/obj/item/tool/shovel/one_material/Initialize(ml, material_key, _handle_material, _binding_material, override_tool_qualities, override_tool_properties)
+ return ..(ml, material_key, material_key, _binding_material, override_tool_qualities, override_tool_properties)
+
/obj/item/tool/spade
name = "spade"
desc = "A small tool for digging and moving dirt."
diff --git a/html/changelog.html b/html/changelog.html
index 8373e672193..61796b46617 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -142,20 +142,6 @@ MistakeNot4892 updated:
Gibbing mobs or bodyparts will now drop usable meat and bone.
You can now craft hand axes.
-
- 30 April 2024
- MistakeNot4892 updated:
-
- - Growns can be processed into different forms based on the grown. Apples will be sliced, potatoes will be turned into sticks, and carrots will be chopped. You can also turn sticks into chopped veggies by using a knife again.
- - You can now extract seeds from growns using Botany skill and a sharp knife.
- - Potatoes can be planted directly without extracting seeds.
-
-
- 26 April 2024
- MistakeNot4892 updated:
-
- - Non-high vis accessories on clothes will show as part of the examine string.
-