diff --git a/code/__defines/ai.dm b/code/__defines/ai.dm new file mode 100644 index 00000000000..2123e2e8839 --- /dev/null +++ b/code/__defines/ai.dm @@ -0,0 +1,22 @@ +// TODO: FSM/decl based stances +#define STANCE_NONE /decl/mob_controller_stance/none +#define STANCE_IDLE /decl/mob_controller_stance/idle +#define STANCE_ALERT /decl/mob_controller_stance/alert +#define STANCE_ATTACK /decl/mob_controller_stance/attack +#define STANCE_ATTACKING /decl/mob_controller_stance/attacking +#define STANCE_TIRED /decl/mob_controller_stance/tired +#define STANCE_CONTAINED /decl/mob_controller_stance/contained + //basically 'do nothing' +#define STANCE_COMMANDED_STOP /decl/mob_controller_stance/commanded/stop +//follows a target +#define STANCE_COMMANDED_FOLLOW /decl/mob_controller_stance/commanded/follow +//catch all state for misc commands that need one. +#define STANCE_COMMANDED_MISC /decl/mob_controller_stance/commanded/misc +//we got healing powers yo +#define STANCE_COMMANDED_HEAL /decl/mob_controller_stance/commanded/heal +#define STANCE_COMMANDED_HEALING /decl/mob_controller_stance/commanded/healing + +#define AI_ACTIVITY_IDLE 0 +#define AI_ACTIVITY_MOVING_TO_TARGET 1 +#define AI_ACTIVITY_BUILDING 2 +#define AI_ACTIVITY_REPRODUCING 3 diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 4ede3dbaa6b..b5d82fcb43a 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -19,13 +19,6 @@ #define BORGXRAY BITFLAG(2) #define BORGMATERIAL BITFLAG(3) -#define HOSTILE_STANCE_IDLE 1 -#define HOSTILE_STANCE_ALERT 2 -#define HOSTILE_STANCE_ATTACK 3 -#define HOSTILE_STANCE_ATTACKING 4 -#define HOSTILE_STANCE_TIRED 5 -#define HOSTILE_STANCE_INSIDE 6 - #define LEFT BITFLAG(0) #define RIGHT BITFLAG(1) #define UNDER BITFLAG(2) diff --git a/code/__defines/reactions.dm b/code/__defines/reactions.dm index 5edc12eb62f..d0a32c77acc 100644 --- a/code/__defines/reactions.dm +++ b/code/__defines/reactions.dm @@ -2,4 +2,4 @@ #define REACTION_TYPE_ALLOYING 2 #define REACTION_TYPE_COMPOUND 3 #define REACTION_TYPE_SYNTHESIS 4 -#define REACTION_TYPE_RECIPE 5 \ No newline at end of file +#define REACTION_TYPE_RECIPE 5 diff --git a/code/_helpers/auxtools.dm b/code/_helpers/auxtools.dm index 1950aa4b170..89b922001a6 100644 --- a/code/_helpers/auxtools.dm +++ b/code/_helpers/auxtools.dm @@ -9,13 +9,21 @@ var/global/auxtools_debug_server = world.GetConfig("env", "AUXTOOLS_DEBUG_DLL") /proc/enable_debugging(mode, port) CRASH("auxtools not loaded") -/hook/startup/proc/auxtools_init() +/world/New() + auxtools_init() + return ..() + +/world/proc/auxtools_init() if (global.auxtools_debug_server) call_ext(global.auxtools_debug_server, "auxtools_init")() enable_debugging() return TRUE -/hook/shutdown/proc/auxtools_shutdown() +/world/Del() + auxtools_shutdown() + return ..() + +/world/proc/auxtools_shutdown() if (global.auxtools_debug_server) call_ext(global.auxtools_debug_server, "auxtools_shutdown")() return TRUE diff --git a/code/_helpers/turfs.dm b/code/_helpers/turfs.dm index 55735c967bc..6f3388b912b 100644 --- a/code/_helpers/turfs.dm +++ b/code/_helpers/turfs.dm @@ -114,7 +114,8 @@ if(ignore_background && (source.turf_flags & TURF_FLAG_BACKGROUND)) continue var/old_turf = source.prev_type || base_turf || get_base_turf_by_area(source) - source.ChangeTurf(old_turf, keep_air = !translate_air) + var/turf/changed = source.ChangeTurf(old_turf, keep_air = !translate_air) + changed.prev_type = null //Transports a turf from a source turf to a target turf, moving all of the turf's contents and making the target a copy of the source. //If ignore_background is set to true, turfs with TURF_FLAG_BACKGROUND set will only translate anchored contents. diff --git a/code/_macros.dm b/code/_macros.dm index 31458da86e5..5cba5893825 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -169,6 +169,7 @@ #define SPAN_PINK(X) SPAN_CLASS("font_pink", X) #define SPAN_PALEPINK(X) SPAN_CLASS("font_palepink", X) #define SPAN_SINISTER(X) SPAN_CLASS("sinister", X) +#define SPAN_MODERATE(X) SPAN_CLASS("moderate", X) // placeholders #define SPAN_GOOD(X) SPAN_GREEN(X) #define SPAN_NEUTRAL(X) SPAN_BLUE(X) diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm index 9d4437eac49..ec0965a969f 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -39,8 +39,7 @@ // Receive a mouse drop. // Returns false if the atom is valid for dropping further up the chain, true if the drop has been handled. /atom/proc/receive_mouse_drop(atom/dropping, mob/user, params) - var/mob/living/H = user - if(istype(H) && !H.anchored && can_climb(H) && dropping == user) + if(isliving(user) && !user.anchored && can_climb(user) && dropping == user) do_climb(dropping) return TRUE return FALSE diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index b326cbc2b0f..717de7a5e81 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -61,7 +61,10 @@ avoid code duplication. This includes items that may sometimes act as a standard return TRUE if(try_butcher_in_place(user, used_item)) return TRUE - return used_item.use_on_mob(src, user) + var/oldhealth = current_health + . = used_item.use_on_mob(src, user) + if(used_item.force && istype(ai) && current_health < oldhealth) + ai.retaliate(user) /mob/living/human/attackby(obj/item/I, mob/user) diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index a43100cd6dd..37f32ced98d 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -87,9 +87,9 @@ if(!do_after(src, attack_delay, A) || !Adjacent(A)) visible_message(SPAN_NOTICE("\The [src] misses [G.his] attack on \the [A]!")) animate(src, pixel_x = default_pixel_x, pixel_y = default_pixel_y, time = 2) // reset wherever the attack animation got us to. - MoveToTarget(TRUE) // Restart hostile mob tracking. + ai?.move_to_target(TRUE) // Restart hostile mob tracking. return TRUE - MoveToTarget(TRUE) // Restart hostile mob tracking. + ai?.move_to_target(TRUE) // Restart hostile mob tracking. if(ismob(A)) // Clientless mobs are too dum to move away, so they can be missed. var/mob/mob = A @@ -97,7 +97,9 @@ visible_message(SPAN_NOTICE("\The [src] misses [G.his] attack on \the [A]!")) return TRUE - return A.attackby(attacking_with, src) + . = A.attackby(attacking_with, src) + if(isliving(A)) + apply_attack_effects(A) // Attack hand but for simple animals /atom/proc/attack_animal(mob/user) diff --git a/code/controllers/hooks-defs.dm b/code/controllers/hooks-defs.dm index f807f5b88db..696352c85eb 100644 --- a/code/controllers/hooks-defs.dm +++ b/code/controllers/hooks-defs.dm @@ -33,80 +33,3 @@ * Called in world.dm prior to the parent call in world/Reboot. */ /hook/reboot - -/** - * Death hook. - * Called in death.dm when someone dies. - * Parameters: var/mob/living/human, var/gibbed - */ -/hook/death - -/** - * Cloning hook. - * Called in cloning.dm when someone is brought back by the wonders of modern science. - * Parameters: var/mob/living/human - */ -/hook/clone - -/** - * Debrained hook. - * Called in brain_item.dm when someone gets debrained. - * Parameters: var/obj/item/organ/internal/brain - */ -/hook/debrain - -/** - * Borged hook. - * Called in robot_parts.dm when someone gets turned into a cyborg. - * Parameters: var/mob/living/silicon/robot - */ -/hook/borgify - -/** - * Payroll revoked hook. - * Called in Accounts_DB.dm when someone's payroll is stolen at the Accounts terminal. - * Parameters: var/datum/money_account - */ -/hook/revoke_payroll - -/** - * Account suspension hook. - * Called in Accounts_DB.dm when someone's account is suspended or unsuspended at the Accounts terminal. - * Parameters: var/datum/money_account - */ -/hook/change_account_status - -/** - * Employee reassignment hook. - * Called in card.dm when someone's card is reassigned at the HoP's desk. - * Parameters: var/obj/item/card/id - */ -/hook/reassign_employee - -/** - * Employee terminated hook. - * Called in card.dm when someone's card is terminated at the HoP's desk. - * Parameters: var/obj/item/card/id - */ -/hook/terminate_employee - -/** - * Crate sold hook. - * Called in supplyshuttle.dm when a crate is sold on the shuttle. - * Parameters: var/obj/structure/closet/crate/sold, var/area/shuttle - */ -/hook/sell_crate - -/** - * Player latejoin hook. - * Called in new_player.dm when a player joins the round after it has started. - * Parameters: var/datum/job/job, var/mob/living/character - */ -/hook/player_latejoin - -/** - * Submap join hook. - * Called in submap_join.dm when a player joins a submap. - * Parameters: var/datum/submap/submap, var/datum/job/job, var/mob/living/character - */ -/hook/submap_join \ No newline at end of file diff --git a/code/controllers/subsystems/supply.dm b/code/controllers/subsystems/supply.dm index a1acfc221bb..baf6e4c614d 100644 --- a/code/controllers/subsystems/supply.dm +++ b/code/controllers/subsystems/supply.dm @@ -84,9 +84,9 @@ SUBSYSTEM_DEF(supply) for(var/atom/movable/AM in subarea) if(AM.anchored) continue - if(istype(AM, /obj/structure/closet/crate/)) + if(istype(AM, /obj/structure/closet/crate)) var/obj/structure/closet/crate/CR = AM - callHook("sell_crate", list(CR, subarea)) + RAISE_EVENT(/decl/observ/crate_sold, subarea, CR) add_points_from_source(CR.get_single_monetary_worth() * crate_return_rebate * 0.1, "crate") var/find_slip = 1 diff --git a/code/datums/ai/_ai.dm b/code/datums/ai/_ai.dm new file mode 100644 index 00000000000..22ff8d65539 --- /dev/null +++ b/code/datums/ai/_ai.dm @@ -0,0 +1,294 @@ +/* Notes/thoughts/assumptions, June 2024: + * + * 1. AI should not implement any bespoke mob logic within the proc it uses + * to trigger or respond to game events. It should share entrypoints with + * action performed by players and should respect the same intents, etc. + * that players have to manage, through the same procs players use. This + * should mean that players can be slotted into the pilot seat of any mob, + * suspending AI behavior, and should then be able to freely use any of the + * behaviors the AI can use with that mob (special attacks, burrowing, so on). + * + * 2. Where possible, attacks/effects/etc should use existing systems (see the + * natural attack item simple_animal uses for example) for a similar reason to + * the above. Ideally this should also extend to ranged attacks in the future. + * + */ + +/datum/mob_controller + /// The parent mob we control. + var/mob/living/body + /// Type of mob this AI applies to. + var/expected_type = /mob/living + + // WANDERING + /// How many life ticks should pass before we wander? + var/turns_per_wander = 2 + /// How many life ticks have passed since our last wander? + var/turns_since_wander = 0 + /// Use this to temporarely stop random movement or to if you write special movement code for animals. + var/stop_wander = FALSE + /// Does the mob wander around when idle? + var/do_wander = TRUE + /// When set to 1 this stops the animal from moving when someone is grabbing it. + var/stop_wander_when_pulled = TRUE + + // SPEAKING/EMOTING + /// A prob chance of speaking. + var/speak_chance = 0 + /// Strings shown when this mob speaks and is not understood. + var/list/emote_speech + /// Hearable emotes that this mob can randomly perform. + var/list/emote_hear + /// 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 + /// What directions can we wander in? Uses global.cardinal if unset. + var/list/wander_directions + + /// Can we automatically escape from buckling? + var/can_escape_buckles = FALSE + + /// What is our current general attitude and demeanor? + var/stance = STANCE_NONE + /// What are we busy with currently? + var/current_activity = AI_ACTIVITY_IDLE + + /// Who are we friends with? Lazylist of weakrefs. + var/list/_friends + /// Who are our sworn enemies? Lazylist of weakrefs. + var/list/_enemies + + /// Aggressive AI var; defined here for reference without casting. + var/try_destroy_surroundings = FALSE + +/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(SSmob_ai, src) + +/datum/mob_controller/Destroy() + LAZYCLEARLIST(_friends) + LAZYCLEARLIST(_enemies) + if(is_processing) + STOP_PROCESSING(SSmob_ai, src) + if(body) + if(body.ai == src) + body.ai = null + body = null + . = ..() + +/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 + +/datum/mob_controller/proc/can_process() + if(!body || !body.loc || ((body.client || body.mind) && !(body.status_flags & ENABLE_AI))) + return FALSE + if(body.stat == DEAD) + return FALSE + return TRUE + +/datum/mob_controller/Process() + if(can_process()) + do_process() + +/datum/mob_controller/proc/pause() + if(is_processing) + STOP_PROCESSING(SSmob_ai, src) + return TRUE + return FALSE + +/datum/mob_controller/proc/resume() + if(!is_processing) + START_PROCESSING(SSmob_ai, src) + return TRUE + return FALSE + +// This is the place to actually do work in the AI. +/datum/mob_controller/proc/do_process() + SHOULD_CALL_PARENT(TRUE) + if(!body || QDELETED(body)) + return + if(!body.stat) + try_unbuckle() + try_wander() + try_bark() + +// The mob will try to unbuckle itself from nets, beds, chairs, etc. +/datum/mob_controller/proc/try_unbuckle() + if(body.buckled && can_escape_buckles) + if(istype(body.buckled, /obj/effect/energy_net)) + var/obj/effect/energy_net/Net = body.buckled + Net.escape_net(body) + else if(prob(25)) + body.buckled.unbuckle_mob(body) + else if(prob(25)) + body.visible_message(SPAN_WARNING("\The [body] struggles against \the [body.buckled]!")) + + +/datum/mob_controller/proc/get_activity() + return current_activity + +/datum/mob_controller/proc/set_activity(new_activity) + if(current_activity != new_activity) + current_activity = new_activity + return TRUE + return FALSE + +// The mob will periodically sit up or step 1 tile in a random direction. +/datum/mob_controller/proc/try_wander() + //Movement + if(body.current_posture?.prone) + if(!body.incapacitated()) + body.set_posture(/decl/posture/standing) + else if(!stop_wander && !body.buckled_mob && do_wander && !body.anchored) + if(isturf(body.loc) && !body.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_wander_when_pulled) || !LAZYLEN(body.grabbed_by))) //Some animals don't move when pulled + var/direction = pick(wander_directions || global.cardinal) + var/turf/move_to = get_step(body.loc, direction) + if(body.turf_is_safe(move_to)) + body.SelfMove(direction) + turns_since_wander = 0 + +// The mob will periodically make a noise or perform an emote. +/datum/mob_controller/proc/try_bark() + //Speaking + if(prob(speak_chance)) + var/action = pick( + LAZYLEN(emote_speech); "emote_speech", + LAZYLEN(emote_hear); "emote_hear", + LAZYLEN(emote_see); "emote_see" + ) + var/do_emote + var/emote_type = VISIBLE_MESSAGE + switch(action) + if("emote_speech") + if(length(emote_speech)) + body.say(pick(emote_speech)) + if("emote_hear") + do_emote = SAFEPICK(emote_hear) + emote_type = AUDIBLE_MESSAGE + if("emote_see") + do_emote = SAFEPICK(emote_see) + + if(istext(do_emote)) + body.custom_emote(emote_type, "[do_emote].") + else if(ispath(do_emote, /decl/emote)) + body.emote(do_emote) + +/datum/mob_controller/proc/get_target() + return null + +/datum/mob_controller/proc/set_target(atom/new_target) + return + +/datum/mob_controller/proc/find_target() + return + +/datum/mob_controller/proc/valid_target(var/atom/A) + return + +/datum/mob_controller/proc/move_to_target(var/move_only = FALSE) + return + +/datum/mob_controller/proc/stop_wandering() + stop_wander = TRUE + +/datum/mob_controller/proc/resume_wandering() + stop_wander = FALSE + +/datum/mob_controller/proc/set_stance(new_stance) + if(stance != new_stance) + stance = new_stance + return TRUE + return FALSE + +/datum/mob_controller/proc/get_stance() + return stance + +/datum/mob_controller/proc/list_targets(var/dist = 7) + return + +/datum/mob_controller/proc/open_fire() + return + +/datum/mob_controller/proc/retaliate(atom/source) + SHOULD_CALL_PARENT(TRUE) + if(!istype(body) || body.stat) + return FALSE + if(isliving(source)) + remove_friend(source) + return TRUE + +/datum/mob_controller/proc/destroy_surroundings() + return + +/datum/mob_controller/proc/lose_target() + return + +/datum/mob_controller/proc/lost_target() + return + +/datum/mob_controller/proc/handle_death(gibbed) + return + +/datum/mob_controller/proc/pacify(mob/user) + lose_target() + add_friend(user) + +// General-purpose memorise proc, used by /commanded +/datum/mob_controller/proc/memorise(mob/speaker, message) + return + +// General-purpose memory checking proc, used by /faithful_hound +/datum/mob_controller/proc/check_memory(mob/speaker, message) + return FALSE + +// Enemy tracking - used on /aggressive +/datum/mob_controller/proc/get_enemies() + return _enemies + +/datum/mob_controller/proc/add_enemy(mob/enemy) + if(istype(enemy)) + LAZYDISTINCTADD(_enemies, weakref(enemy)) + +/datum/mob_controller/proc/add_enemies(list/enemies) + for(var/thing in enemies) + if(ismob(thing)) + add_friend(thing) + else if(istype(thing, /weakref)) + LAZYDISTINCTADD(_enemies, thing) + +/datum/mob_controller/proc/remove_enemy(mob/enemy) + LAZYREMOVE(_enemies, weakref(enemy)) + +/datum/mob_controller/proc/set_enemies(list/new_enemies) + _enemies = new_enemies + +/datum/mob_controller/proc/is_enemy(mob/enemy) + . = istype(enemy) && LAZYLEN(_enemies) && (weakref(enemy) in _enemies) + +/datum/mob_controller/proc/clear_enemies() + LAZYCLEARLIST(_enemies) + +// Friend tracking - used on /aggressive. +/datum/mob_controller/proc/get_friends() + return _friends + +/datum/mob_controller/proc/add_friend(mob/friend) + if(istype(friend)) + LAZYDISTINCTADD(_friends, weakref(friend)) + return TRUE + return FALSE + +/datum/mob_controller/proc/remove_friend(mob/friend) + LAZYREMOVE(_friends, weakref(friend)) + +/datum/mob_controller/proc/set_friends(list/new_friends) + _friends = new_friends + +/datum/mob_controller/proc/is_friend(mob/friend) + . = istype(friend) && LAZYLEN(_friends) && (weakref(friend) in _friends) diff --git a/code/datums/ai/_ai_stance.dm b/code/datums/ai/_ai_stance.dm new file mode 100644 index 00000000000..ca018169811 --- /dev/null +++ b/code/datums/ai/_ai_stance.dm @@ -0,0 +1,30 @@ +// Stub type for future expansion/logic encapsulation. +/decl/mob_controller_stance + abstract_type = /decl/mob_controller_stance + +/decl/mob_controller_stance/none + +/decl/mob_controller_stance/idle + +/decl/mob_controller_stance/alert + +/decl/mob_controller_stance/attack + +/decl/mob_controller_stance/attacking + +/decl/mob_controller_stance/tired + +/decl/mob_controller_stance/contained + +/decl/mob_controller_stance/commanded + abstract_type = /decl/mob_controller_stance/commanded + +/decl/mob_controller_stance/commanded/stop + +/decl/mob_controller_stance/commanded/follow + +/decl/mob_controller_stance/commanded/misc + +/decl/mob_controller_stance/commanded/heal + +/decl/mob_controller_stance/commanded/healing diff --git a/code/datums/ai/aggressive.dm b/code/datums/ai/aggressive.dm new file mode 100644 index 00000000000..7dcc9280f9f --- /dev/null +++ b/code/datums/ai/aggressive.dm @@ -0,0 +1,276 @@ +/datum/mob_controller/aggressive + stance = STANCE_IDLE + stop_wander_when_pulled = FALSE + try_destroy_surroundings = TRUE + var/attack_same_faction = FALSE + var/only_attack_enemies = FALSE + var/break_stuff_probability = 10 + var/weakref/target_ref + +/datum/mob_controller/aggressive/set_target(atom/new_target) + var/weakref/new_target_ref = weakref(new_target) + if(target_ref != new_target_ref) + target_ref = new_target_ref + return TRUE + return FALSE + +/datum/mob_controller/aggressive/get_target() + if(isnull(target_ref)) + return null + var/atom/target = target_ref?.resolve() + if(!istype(target) || QDELETED(target)) + set_target(null) + return null + return target + +/datum/mob_controller/aggressive/Destroy() + set_target(null) + return ..() + +/datum/mob_controller/aggressive/do_process() + . = ..() + if(QDELETED(body) || body.stat) + return + + if(!body.can_act()) + body.stop_automove() + set_stance(get_target() ? STANCE_ATTACK : STANCE_IDLE) + return + + if(isturf(body.loc) && !body.buckled) + switch(stance) + + if(STANCE_IDLE) + set_target(find_target()) + set_stance(STANCE_ATTACK) + + if(STANCE_ATTACK) + body.face_atom(get_target()) + if(try_destroy_surroundings) + destroy_surroundings() + move_to_target() + + if(STANCE_ATTACKING) + body.face_atom(get_target()) + if(try_destroy_surroundings) + destroy_surroundings() + handle_attacking_target() + + if(STANCE_CONTAINED) //we aren't inside something so just switch + set_stance(STANCE_IDLE) + + else if(get_stance() != STANCE_CONTAINED) + set_stance(STANCE_CONTAINED) + body.stop_automove() + set_target(null) + +/datum/mob_controller/aggressive/proc/attackable(target_mob) + if (isliving(target_mob)) + var/mob/living/L = target_mob + if(L.stat) + return FALSE + return TRUE + +/datum/mob_controller/aggressive/proc/handle_attacking_target() + stop_wandering() + var/atom/target = get_target() + if(!istype(target) || !attackable(target) || !(target in list_targets(10))) // consider replacing this list_targets() call with a distance or LOS check + lose_target() + return FALSE + if (ishuman(target)) + var/mob/living/human/H = target + if (H.is_cloaked()) + lose_target() + return FALSE + if(body.next_move >= world.time) + return FALSE + if(get_dist(body, target) > 1) + move_to_target() + return FALSE + //Attacking + attack_target() + return TRUE + +/datum/mob_controller/aggressive/proc/attack_target() + var/atom/target = get_target() + if(!istype(target)) + lose_target() + return + if(isliving(target) && body.buckled_mob == target && (!body.faction || body.buckled_mob.faction != body.faction)) + body.visible_message(SPAN_DANGER("\The [body] attempts to unseat \the [body.buckled_mob]!")) + body.set_dir(pick(global.cardinal)) + body.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(prob(33)) + body.unbuckle_mob() + if(body.buckled_mob != target && !QDELETED(target)) + to_chat(target, SPAN_DANGER("You are thrown off \the [body]!")) + var/mob/living/victim = target + SET_STATUS_MAX(victim, STAT_WEAK, 3) + return target + if(body.Adjacent(target)) + body.a_intent = I_HURT + body.ClickOn(target) + return target + +/datum/mob_controller/aggressive/destroy_surroundings() + + if(!body.can_act()) + return + + // If we're not hunting something, don't destroy stuff. + var/atom/target = get_target() + if(!istype(target)) + return + + // Not breaking stuff, or already adjacent to a target. + if(!prob(break_stuff_probability) || body.Adjacent(target)) + return + + // Try to get our next step towards the target. + body.face_atom(target) + var/turf/targ = get_step_towards(body, target) + if(!targ) + return + + // Attack anything on the target turf. + var/obj/effect/shield/S = locate(/obj/effect/shield) in targ + if(S && S.gen && S.gen.check_flag(MODEFLAG_NONHUMANS)) + body.a_intent = I_HURT + body.ClickOn(S) + return + + // Hostile mobs will bash through these in order with their natural weapon + // Note that airlocks and blast doors are handled separately below. + // TODO: mobs should destroy powered/unforceable doors before trying to pry them. + var/static/list/valid_obstacles_by_priority = list( + /obj/structure/window, + /obj/structure/closet, + /obj/machinery/door/window, + /obj/structure/table, + /obj/structure/grille, + /obj/structure/barricade, + /obj/structure/wall_frame, + /obj/structure/railing + ) + + for(var/type in valid_obstacles_by_priority) + var/obj/obstacle = locate(type) in targ + if(obstacle) + body.a_intent = I_HURT + body.ClickOn(obstacle) + return + + if(body.can_pry_door()) + for(var/obj/machinery/door/obstacle in targ) + if(obstacle.density) + if(!obstacle.can_open(1)) + return + body.face_atom(obstacle) + body.pry_door(obstacle, (obstacle.pry_mod * body.get_door_pry_time())) + return + +/datum/mob_controller/aggressive/retaliate(atom/source) + + if(!(. = ..())) + return + + if(!only_attack_enemies) + if(source) + set_target(source) + move_to_target(move_only = TRUE) + return + + var/list/allies + var/list/around = view(body, 7) + for(var/atom/movable/A in around) + if(A == body || !isliving(A)) + continue + var/mob/living/M = A + if(attack_same_faction || M.faction != body.faction) + add_enemy(M) + else if(istype(M.ai)) + LAZYADD(allies, M.ai) + + var/list/enemies = get_enemies() + if(LAZYLEN(enemies) && LAZYLEN(allies)) + for(var/datum/mob_controller/ally as anything in allies) + ally.add_enemies(enemies) + +/datum/mob_controller/aggressive/move_to_target(var/move_only = FALSE) + if(!body.can_act()) + return + if(HAS_STATUS(body, STAT_CONFUSE)) + body.start_automove(pick(orange(2, body))) + return + stop_wandering() + var/atom/target = get_target() + if(!istype(target) || !attackable(target) || !(target in list_targets(10))) + lose_target() + return + if(body.has_ranged_attack() && get_dist(body, target) <= body.get_ranged_attack_distance() && !move_only) + body.stop_automove() + open_fire() + return + set_stance(STANCE_ATTACKING) + body.start_automove(target) + +/datum/mob_controller/aggressive/list_targets(var/dist = 7) + // Base hostile mobs will just destroy everything in view. + // Mobs with an enemy list will filter the view by their enemies. + if(!only_attack_enemies) + return hearers(body, dist)-body + var/list/enemies = get_enemies() + if(!LAZYLEN(enemies)) + return + var/list/possible_targets = hearers(body, dist)-body + if(!length(possible_targets)) + return + for(var/weakref/enemy in enemies) // Remove all entries that aren't in enemies + var/M = enemy.resolve() + if(M in possible_targets) + LAZYDISTINCTADD(., M) + +/datum/mob_controller/aggressive/find_target() + if(!body.can_act() || !body.faction) + return null + resume_wandering() + for(var/atom/A in list_targets(10)) + if(valid_target(A)) + set_stance(STANCE_ATTACK) + body.face_atom(A) + return A + +/datum/mob_controller/aggressive/valid_target(var/atom/A) + if(A == body) + return FALSE + if(ismob(A)) + var/mob/M = A + if(M.faction == body.faction && !attack_same_faction) + return FALSE + else if(weakref(M) in get_friends()) + return FALSE + if(M.stat) + return FALSE + if(ishuman(M)) + var/mob/living/human/H = M + if (H.is_cloaked()) + return FALSE + return TRUE + +/datum/mob_controller/aggressive/open_fire() + if(!body.can_act()) + return FALSE + body.handle_ranged_attack(get_target()) + return TRUE + +/datum/mob_controller/aggressive/lose_target() + set_target(null) + lost_target() + +/datum/mob_controller/aggressive/lost_target() + set_stance(STANCE_IDLE) + body.stop_automove() + +/datum/mob_controller/aggressive/pacify(mob/user) + ..() + attack_same_faction = FALSE diff --git a/code/datums/ai/ai.dm b/code/datums/ai/ai.dm deleted file mode 100644 index 8373aa01c26..00000000000 --- a/code/datums/ai/ai.dm +++ /dev/null @@ -1,47 +0,0 @@ -/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/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(SSmob_ai, src) - -/datum/mob_controller/Destroy() - STOP_PROCESSING(SSmob_ai, src) - if(body) - if(body.ai == src) - body.ai = null - body = null - . = ..() - -/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) - return FALSE - if(body.stat == DEAD) - return FALSE - return TRUE - -/datum/mob_controller/Process() - if(!can_process()) - return - - var/time_elapsed = wait_for - world.time - wait_for = world.time + run_interval - do_process(time_elapsed) - -// This is the place to actually do work in the AI. -/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/beast.dm b/code/datums/ai/beast.dm new file mode 100644 index 00000000000..f0c081734ef --- /dev/null +++ b/code/datums/ai/beast.dm @@ -0,0 +1,50 @@ + +/datum/mob_controller/aggressive/beast + expected_type = /mob/living/simple_animal/hostile/beast + only_attack_enemies = TRUE + var/list/prey + +/datum/mob_controller/aggressive/beast/do_process(time_elapsed) + + . = ..() + + var/mob/living/simple_animal/hostile/beast/beast = body + if(!istype(beast)) + return + + var/nut = beast.get_nutrition() + var/max_nut = beast.get_max_nutrition() + if(nut > max_nut * 0.75 || beast.incapacitated()) + LAZYCLEARLIST(prey) + return + for(var/mob/living/simple_animal/S in range(beast,1)) + if(S == beast) + continue + if(S.stat != DEAD) + continue + beast.visible_message(SPAN_DANGER("\The [beast] consumes the body of \the [S]!")) + var/turf/T = get_turf(S) + var/remains_type = S.get_remains_type() + if(remains_type) + var/obj/item/remains/X = new remains_type(T) + X.desc += "These look like they belonged to \a [S.name]." + beast.adjust_nutrition(5 * S.get_max_health()) + if(prob(5)) + S.gib() + else + qdel(S) + break + +/datum/mob_controller/aggressive/beast/list_targets(var/dist = 7) + . = ..() + if(!length(.)) + if(LAZYLEN(prey)) + . = list() + for(var/weakref/W in prey) + var/mob/M = W.resolve() + if(M) + . |= M + else if(body.get_nutrition() < body.get_max_nutrition() * 0.75) //time to look for some food + for(var/mob/living/L in view(body, dist)) + if(attack_same_faction || L.faction != body.faction) + LAZYDISTINCTADD(prey, weakref(L)) diff --git a/code/datums/ai/commanded.dm b/code/datums/ai/commanded.dm new file mode 100644 index 00000000000..8937a654565 --- /dev/null +++ b/code/datums/ai/commanded.dm @@ -0,0 +1,151 @@ +/datum/mob_controller/aggressive/commanded + stance = STANCE_COMMANDED_STOP + var/list/command_buffer = list() + var/list/known_commands = list("stay", "stop", "attack", "follow") + /// undisputed master. Their commands hold ultimate sway and ultimate power. + var/mob/master = null + /// Lazylist of acceptable target weakrefs as designated by our master (or 'everyone'). + var/list/_allowed_targets + /// whether or not they will attack us if we attack them like some kinda dick. + var/retribution = TRUE + +/datum/mob_controller/aggressive/commanded/do_process(time_elapsed) + ..() + while(command_buffer.len > 1) + var/mob/speaker = command_buffer[1] + var/message = command_buffer[2] + var/filtered_name = lowertext(html_decode(body.name)) + if(dd_hasprefix(message,filtered_name) || dd_hasprefix(message,"everyone") || dd_hasprefix(message, "everybody")) //in case somebody wants to command 8 bears at once. + var/substring = copytext(message,length(filtered_name)+1) //get rid of the name. + listen(speaker,substring) + command_buffer.Remove(command_buffer[1],command_buffer[2]) + switch(stance) + if(STANCE_COMMANDED_FOLLOW) + follow_target() + if(STANCE_COMMANDED_STOP) + commanded_stop() + +/datum/mob_controller/aggressive/commanded/proc/listen(var/mob/speaker, var/message) + for(var/command in known_commands) + if(findtext(message, command)) + switch(command) + if("stay") + if(stay_command(speaker,message)) //find a valid command? Stop. Dont try and find more. + break + if("stop") + if(stop_command(speaker,message)) + break + if("attack") + if(attack_command(speaker,message)) + break + if("follow") + if(follow_command(speaker,message)) + break + else + misc_command(speaker,message) //for specific commands + return TRUE + +/datum/mob_controller/aggressive/commanded/proc/attack_command(var/mob/speaker,var/message) + set_target(null) //want me to attack something? Well I better forget my old target. + set_stance(STANCE_IDLE) + body.stop_automove() + if(message == "attack" || findtext(message,"everyone") || findtext(message,"anybody") || findtext(message, "somebody") || findtext(message, "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 + return 1 + var/list/targets = get_targets_by_name(message) + if(length(targets)) + LAZYDISTINCTADD(_allowed_targets, targets) + return targets.len != 0 + +/datum/mob_controller/aggressive/commanded/proc/stay_command(var/mob/speaker,var/message) + set_target(null) + set_stance(STANCE_COMMANDED_STOP) + stop_wandering() + body.stop_automove() + return 1 + +/datum/mob_controller/aggressive/commanded/proc/stop_command(var/mob/speaker,var/message) + LAZYCLEARLIST(_allowed_targets) + body.stop_automove() + set_target(null) + set_stance(STANCE_IDLE) + resume_wandering() + return 1 + +/datum/mob_controller/aggressive/commanded/proc/follow_command(var/mob/speaker,var/message) + //we can assume 'stop following' is handled by stop_command + if(findtext(message,"me")) + set_stance(STANCE_COMMANDED_FOLLOW) + set_target(speaker) //this wont bite me in the ass later. + return 1 + var/list/targets = get_targets_by_name(message) + if(LAZYLEN(targets) != 1) //CONFUSED. WHO DO I FOLLOW? + return 0 + var/weakref/target_ref = targets[1] + set_target(target_ref.resolve()) //YEAH GOOD IDEA + set_stance(STANCE_COMMANDED_FOLLOW) //GOT SOMEBODY. BETTER FOLLOW EM. + return 1 + +/datum/mob_controller/aggressive/commanded/proc/misc_command(var/mob/speaker,var/message) + return 0 + +/datum/mob_controller/aggressive/commanded/proc/follow_target() + if(!body || body.stat) + return + stop_wandering() + var/atom/target = get_target() + if(istype(target) && (target in list_targets(10))) + body.start_automove(target) + +/datum/mob_controller/aggressive/commanded/proc/commanded_stop() //basically a proc that runs whenever we are asked to stay put. Probably going to remain unused. + return + +//returns a list of everybody we wanna do stuff with. +/datum/mob_controller/aggressive/commanded/proc/get_targets_by_name(var/message, var/filter_friendlies = 0) + var/list/possible_targets = hearers(body, 10) + for(var/mob/M in possible_targets) + if((filter_friendlies && is_friend(M)) || M.faction == body.faction || M == master) + continue + var/found = 0 + if(findtext(message, "[M]")) + found = 1 + else + var/list/parsed_name = splittext(replace_characters(lowertext(html_decode("[M]")),list("-"=" ", "."=" ", "," = " ", "'" = " ")), " ") //this big MESS is basically 'turn this into words, no punctuation, lowercase so we can check first name/last name/etc' + for(var/a in parsed_name) + if(a == "the" || length(a) < 2) //get rid of shit words. + continue + if(findtext(message,"[a]")) + found = 1 + break + if(found) + LAZYADD(., weakref(M)) + +/datum/mob_controller/aggressive/commanded/find_target() + if(!LAZYLEN(_allowed_targets)) + return null + var/mode = "specific" + if(LAZYACCESS(_allowed_targets, 1) == "everyone") //we have been given the golden gift of murdering everything. Except our master, of course. And our friends. So just mostly everyone. + mode = "everyone" + for(var/atom/A in list_targets(10)) + if(A == src) + continue + if(isliving(A)) + var/mob/M = A + if(M.stat || M == master || is_friend(M)) + continue + if(mode == "specific" && !(weakref(A) in _allowed_targets)) + continue + set_stance(STANCE_ATTACK) + return A + +/datum/mob_controller/aggressive/commanded/retaliate(atom/source) + //assume he wants to hurt us. + if(isliving(source) && !retribution) + return + if((. = ..()) && isliving(source)) + LAZYDISTINCTADD(_allowed_targets, weakref(source)) + +/datum/mob_controller/aggressive/commanded/memorise(mob/speaker, message) + if(is_friend(speaker) || speaker == master) + command_buffer.Add(speaker) + command_buffer.Add(lowertext(html_decode(message))) diff --git a/code/datums/ai/human.dm b/code/datums/ai/human.dm index 7c2eda8f136..7e34363d9a3 100644 --- a/code/datums/ai/human.dm +++ b/code/datums/ai/human.dm @@ -1,22 +1,25 @@ /datum/mob_controller/human - name = "human" expected_type = /mob/living/human + do_wander = FALSE /datum/mob_controller/human/do_process(var/time_elapsed) + var/mob/living/human/H = body if(H.stat != CONSCIOUS) return - if(H.get_shock() && H.shock_stage < 40 && prob(3)) + . = ..() + + if(H.get_shock() && H.shock_stage < 40 && prob(1.5)) H.emote(pick(/decl/emote/audible/moan, /decl/emote/audible/groan)) - if(H.shock_stage > 10 && prob(3)) + if(H.shock_stage > 10 && prob(1.5)) H.emote(pick(/decl/emote/audible/cry, /decl/emote/audible/whimper)) - if(H.shock_stage >= 40 && prob(3)) + if(H.shock_stage >= 40 && prob(1.5)) H.emote(/decl/emote/audible/scream) - if(!H.restrained() && H.current_posture.prone && H.shock_stage >= 60 && prob(3)) + if(!H.restrained() && H.current_posture.prone && H.shock_stage >= 60 && prob(1.5)) H.custom_emote("thrashes in agony") if(!H.restrained() && H.shock_stage < 40 && prob(3)) @@ -42,7 +45,8 @@ H.custom_emote("rubs [G.his] [damaged_organ.name] carefully.") for(var/obj/item/organ/I in H.get_internal_organs()) - if((I.status & ORGAN_DEAD) || BP_IS_PROSTHETIC(I)) continue - if(I.damage > 2) if(prob(2)) + if((I.status & ORGAN_DEAD) || BP_IS_PROSTHETIC(I)) + continue + if(I.damage > 2 && prob(1)) var/obj/item/organ/external/parent = GET_EXTERNAL_ORGAN(H, I.parent_organ) H.custom_emote("clutches [G.his] [parent.name]!") diff --git a/code/datums/ai/hunter.dm b/code/datums/ai/hunter.dm new file mode 100644 index 00000000000..35c7236eb59 --- /dev/null +++ b/code/datums/ai/hunter.dm @@ -0,0 +1,85 @@ +/datum/mob_controller/passive/hunter + var/weakref/hunt_target + var/next_hunt = 0 + +/datum/mob_controller/passive/hunter/update_targets() + // Fleeing takes precedence. + . = ..() + if(!. && !get_target() && world.time >= next_hunt) // TODO: generalized nutrition process. && body.get_nutrition() < body.get_max_nutrition() * 0.5) + for(var/mob/living/snack in view(body)) //search for a new target + if(can_hunt(snack)) + set_target(snack) + break + + return . || !!get_target() + +/datum/mob_controller/passive/hunter/proc/can_hunt(mob/living/victim) + return !victim.isSynthetic() && (victim.stat == DEAD || victim.get_object_size() < body.get_object_size()) + +/datum/mob_controller/passive/hunter/proc/try_attack_prey(mob/living/prey) + body.a_intent = I_HURT + body.ClickOn(prey) + +/datum/mob_controller/passive/hunter/proc/consume_prey(mob/living/prey) + body.visible_message(SPAN_DANGER("\The [body] consumes the body of \the [prey]!")) + var/remains_type = prey.get_remains_type() + if(remains_type) + var/obj/item/remains/remains = new remains_type(get_turf(prey)) + remains.desc += "These look like they belonged to \a [prey.name]." + body.adjust_nutrition(5 * prey.get_max_health()) + next_hunt = world.time + rand(15 MINUTES, 30 MINUTES) + if(prob(5)) + prey.gib() + else + qdel(prey) + +/datum/mob_controller/passive/hunter/get_target(atom/new_target) + if(isnull(hunt_target)) + return null + var/mob/living/prey = hunt_target.resolve() + if(!istype(prey) || QDELETED(prey)) + set_target(null) + return null + return prey + +/datum/mob_controller/passive/hunter/set_target(atom/new_target) + if(isnull(new_target) || isliving(new_target)) + hunt_target = new_target ? weakref(new_target) : null + return TRUE + return FALSE + +/datum/mob_controller/passive/hunter/do_process(time_elapsed) + + ..() + + if(!body || body.incapacitated() || body.current_posture?.prone || body.buckled || flee_target || !get_target()) + return + + var/mob/living/target = get_target() + if(!istype(target) || QDELETED(target) || !(target in view(body))) + set_target(null) + resume_wandering() + return + + // Find or pursue the target. + if(!body.Adjacent(target)) + stop_wandering() + body.start_automove(target) + return + + // Hunt/consume the target. + if(target.stat != DEAD) + try_attack_prey(target) + + if(QDELETED(target)) + set_target(null) + resume_wandering() + return + + if(target.stat != DEAD) + return + + // Eat the mob. + set_target(null) + resume_wandering() + consume_prey(target) diff --git a/code/datums/ai/monkey.dm b/code/datums/ai/monkey.dm index 6f902d87597..5023e590d81 100644 --- a/code/datums/ai/monkey.dm +++ b/code/datums/ai/monkey.dm @@ -1,23 +1,21 @@ /datum/mob_controller/monkey - name = "monkey" expected_type = /mob/living/human - var/list/no_touchie = list( + var/static/list/no_touchie = list( /obj/item/mirror, /obj/structure/mirror ) /datum/mob_controller/monkey/do_process(var/time_elapsed) + + . = ..() if(body.incapacitated()) return - if(prob(33) && isturf(body.loc) && !LAZYLEN(body.grabbed_by)) //won't move if being pulled - body.SelfMove(pick(global.cardinal)) - var/obj/held = body.get_active_held_item() if(held && prob(1)) var/turf/T = get_random_turf_in_range(body, 7, 2) if(T) - if(istype(held, /obj/item/gun) && prob(80)) + if(istype(held, /obj/item/gun) && prob(40)) var/obj/item/gun/G = held G.Fire(T, body) else @@ -25,7 +23,7 @@ else body.try_unequip(held) - if(!held && !body.restrained() && prob(5)) + if(!held && !body.restrained() && prob(2.5)) var/list/touchables = list() for(var/obj/O in range(1,get_turf(body))) if(O.simulated && CanPhysicallyInteractWith(body, O) && !is_type_in_list(O, no_touchie)) diff --git a/code/datums/ai/passive.dm b/code/datums/ai/passive.dm new file mode 100644 index 00000000000..b3320d0bc00 --- /dev/null +++ b/code/datums/ai/passive.dm @@ -0,0 +1,44 @@ +/datum/mob_controller/passive + speak_chance = 0.25 + turns_per_wander = 10 + var/weakref/flee_target + var/turns_since_scan + +/datum/mob_controller/passive/proc/update_targets() + //see if we should stop fleeing + 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) + stop_wandering() + else + flee_target = null + resume_wandering() + return !isnull(flee_target) + +/datum/mob_controller/passive/do_process(time_elapsed) + ..() + + // Handle fleeing from aggressors. + turns_since_scan++ + if (turns_since_scan > 10) + body.stop_automove() + turns_since_scan = 0 + if(update_targets()) + return + + // Handle sleeping or wandering. + if(body.stat == CONSCIOUS && prob(0.25)) + body.set_stat(UNCONSCIOUS) + do_wander = FALSE + speak_chance = 0 + else if(body.stat == UNCONSCIOUS && prob(0.5)) + body.set_stat(CONSCIOUS) + do_wander = TRUE + +/datum/mob_controller/passive/retaliate(atom/source) + if((. = ..())) + source = source || get_turf(body) + if(istype(source)) + flee_target = weakref(source) + turns_since_scan = 10 diff --git a/code/datums/ai/ai_holo.dm b/code/datums/ai_holo.dm similarity index 100% rename from code/datums/ai/ai_holo.dm rename to code/datums/ai_holo.dm diff --git a/code/datums/hostility/hostility.dm b/code/datums/hostility/hostility.dm index 2af98770ec3..b17ad114bd7 100644 --- a/code/datums/hostility/hostility.dm +++ b/code/datums/hostility/hostility.dm @@ -12,13 +12,13 @@ return FALSE if(isliving(target)) - var/mob/living/L = target - if(L.stat) + var/mob/living/target_mob = target + if(target_mob.stat) return FALSE if(isliving(holder)) - var/mob/living/H = holder - if(L.faction == H.faction) + var/mob/holder_mob = holder + if(target_mob.faction == holder_mob.faction) return FALSE return can_special_target(holder, target) diff --git a/code/datums/movement/atom_movable.dm b/code/datums/movement/atom_movable.dm index d9376edae72..de5e46e87b8 100644 --- a/code/datums/movement/atom_movable.dm +++ b/code/datums/movement/atom_movable.dm @@ -1,13 +1,13 @@ // Static movement denial -/datum/movement_handler/no_move/MayMove() +/datum/movement_handler/no_move/MayMove(mob/mover, is_external) return MOVEMENT_STOP // Anchor check -/datum/movement_handler/anchored/MayMove() +/datum/movement_handler/anchored/MayMove(mob/mover, is_external) return host.anchored ? MOVEMENT_STOP : MOVEMENT_PROCEED // Movement relay -/datum/movement_handler/move_relay/DoMove(var/direction, var/mover) +/datum/movement_handler/move_relay/DoMove(direction, mob/mover, is_external) var/atom/movable/AM = host.loc if(!istype(AM)) return @@ -25,13 +25,13 @@ ..() src.delay = max(1, delay) -/datum/movement_handler/delay/DoMove() +/datum/movement_handler/delay/DoMove(direction, mob/mover, is_external) next_move = world.time + delay -/datum/movement_handler/delay/MayMove() +/datum/movement_handler/delay/MayMove(mob/mover, is_external) return world.time >= next_move ? MOVEMENT_PROCEED : MOVEMENT_STOP // Relay self -/datum/movement_handler/move_relay_self/DoMove(var/direction, var/mover) +/datum/movement_handler/move_relay_self/DoMove(direction, mob/mover, is_external) host.relaymove(mover, direction) return MOVEMENT_HANDLED diff --git a/code/datums/movement/mob.dm b/code/datums/movement/mob.dm index 8a2c5dfd130..8736fead6f9 100644 --- a/code/datums/movement/mob.dm +++ b/code/datums/movement/mob.dm @@ -1,5 +1,5 @@ // Admin object possession -/datum/movement_handler/mob/admin_possess/DoMove(var/direction) +/datum/movement_handler/mob/admin_possess/DoMove(direction, mob/mover, is_external) if(QDELETED(mob.control_object)) return MOVEMENT_REMOVE @@ -13,7 +13,7 @@ control_object.set_dir(direction) // Death handling -/datum/movement_handler/mob/death/DoMove(var/direction, var/mob/mover) +/datum/movement_handler/mob/death/DoMove(direction, mob/mover, is_external) if(mob != mover || mob.stat != DEAD) return @@ -25,7 +25,7 @@ mob.ghostize() // Incorporeal/Ghost movement -/datum/movement_handler/mob/incorporeal/DoMove(var/direction, var/mob/mover) +/datum/movement_handler/mob/incorporeal/DoMove(direction, mob/mover, is_external) . = MOVEMENT_HANDLED direction = mob.AdjustMovementDirection(direction, mover) mob.set_glide_size(0) @@ -44,7 +44,7 @@ return // Eye movement -/datum/movement_handler/mob/eye/DoMove(var/direction, var/mob/mover) +/datum/movement_handler/mob/eye/DoMove(direction, mob/mover, is_external) if(IS_NOT_SELF(mover)) // We only care about direct movement return if(!mob.eyeobj) @@ -52,7 +52,7 @@ mob.eyeobj.EyeMove(direction) return MOVEMENT_HANDLED -/datum/movement_handler/mob/eye/MayMove(var/mob/mover, var/is_external) +/datum/movement_handler/mob/eye/MayMove(mob/mover, is_external) if(IS_NOT_SELF(mover)) return MOVEMENT_PROCEED if(is_external) @@ -65,29 +65,26 @@ var/allow_move // Space movement -/datum/movement_handler/mob/space/DoMove(var/direction, var/mob/mover) - if(!mob.has_gravity()) - if(!allow_move) - return MOVEMENT_HANDLED - if(!mob.space_do_move(allow_move, direction)) - return MOVEMENT_HANDLED +/datum/movement_handler/mob/space/DoMove(direction, mob/mover, is_external) + if(mob.has_gravity() || (IS_NOT_SELF(mover) && is_external)) + return + if(!allow_move || !mob.space_do_move(allow_move, direction)) + return MOVEMENT_HANDLED -/datum/movement_handler/mob/space/MayMove(var/mob/mover, var/is_external) +/datum/movement_handler/mob/space/MayMove(mob/mover, is_external) if(IS_NOT_SELF(mover) && is_external) return MOVEMENT_PROCEED - if(!mob.has_gravity()) allow_move = mob.Process_Spacemove(1) if(!allow_move) return MOVEMENT_STOP - return MOVEMENT_PROCEED // Buckle movement -/datum/movement_handler/mob/buckle_relay/DoMove(var/direction, var/mover) +/datum/movement_handler/mob/buckle_relay/DoMove(direction, mob/mover, is_external) return mob?.buckled?.handle_buckled_relaymove(src, mob, direction, mover) -/datum/movement_handler/mob/buckle_relay/MayMove(var/mover) +/datum/movement_handler/mob/buckle_relay/MayMove(mob/mover, is_external) if(mob.buckled) return mob.buckled.MayMove(mover, FALSE) ? (MOVEMENT_PROCEED|MOVEMENT_HANDLED) : MOVEMENT_STOP return MOVEMENT_PROCEED @@ -104,7 +101,7 @@ next_move = world.time + delay mob.set_glide_size(delay) -/datum/movement_handler/mob/delay/MayMove(var/mover, var/is_external) +/datum/movement_handler/mob/delay/MayMove(mob/mover, is_external) if(IS_NOT_SELF(mover) && is_external) return MOVEMENT_PROCEED return ((mover && mover != mob) || world.time >= next_move) ? MOVEMENT_PROCEED : MOVEMENT_STOP @@ -116,31 +113,35 @@ next_move += max(0, delay) // Stop effect -/datum/movement_handler/mob/stop_effect/DoMove() - if(MayMove() == MOVEMENT_STOP) +/datum/movement_handler/mob/DoMove(direction, mob/mover, is_external) + if(MayMove(mover, is_external) == MOVEMENT_STOP) return MOVEMENT_HANDLED -/datum/movement_handler/mob/stop_effect/MayMove() +/datum/movement_handler/mob/stop_effect/MayMove(mob/mover, is_external) for(var/obj/effect/stop/S in mob.loc) if(S.victim == mob) return MOVEMENT_STOP return MOVEMENT_PROCEED // Transformation -/datum/movement_handler/mob/transformation/MayMove() +/datum/movement_handler/mob/transformation/MayMove(mob/mover, is_external) return MOVEMENT_STOP // Consciousness - Is the entity trying to conduct the move conscious? -/datum/movement_handler/mob/conscious/MayMove(var/mob/mover) +/datum/movement_handler/mob/conscious/MayMove(mob/mover, is_external) return (mover ? mover.stat == CONSCIOUS : mob.stat == CONSCIOUS) ? MOVEMENT_PROCEED : MOVEMENT_STOP // Along with more physical checks -/datum/movement_handler/mob/physically_capable/MayMove(var/mob/mover) +/datum/movement_handler/mob/physically_capable/MayMove(mob/mover, is_external) // We only check physical capability if the host mob tried to do the moving - return ((mover && mover != mob) || !mob.incapacitated(INCAPACITATION_DISABLED & ~INCAPACITATION_FORCELYING)) ? MOVEMENT_PROCEED : MOVEMENT_STOP + if(mover && mover != mob) + return MOVEMENT_PROCEED + if(mob.incapacitated(INCAPACITATION_DISABLED & ~INCAPACITATION_FORCELYING)) + return MOVEMENT_STOP + return MOVEMENT_PROCEED // Is anything physically preventing movement? -/datum/movement_handler/mob/physically_restrained/MayMove(var/mob/mover) +/datum/movement_handler/mob/physically_restrained/MayMove(mob/mover, is_external) if(istype(mob.buckled) && !mob.buckled.buckle_movable) if(mover == mob) to_chat(mob, SPAN_WARNING("You're buckled to \the [mob.buckled]!")) @@ -166,7 +167,7 @@ return MOVEMENT_PROCEED // Finally... the last of the mob movement junk -/datum/movement_handler/mob/movement/DoMove(var/direction, var/mob/mover) +/datum/movement_handler/mob/movement/DoMove(direction, mob/mover, is_external) . = MOVEMENT_HANDLED if(!mob || mob.moving) @@ -203,7 +204,7 @@ mob.handle_embedded_and_stomach_objects() mob.moving = FALSE -/datum/movement_handler/mob/movement/MayMove(var/mob/mover) +/datum/movement_handler/mob/movement/MayMove(mob/mover, is_external) return IS_SELF(mover) && mob.moving ? MOVEMENT_STOP : MOVEMENT_PROCEED /mob/proc/get_stamina_used_per_step() diff --git a/code/datums/movement/movement.dm b/code/datums/movement/movement.dm index c4982d4b6f5..ffe142f7231 100644 --- a/code/datums/movement/movement.dm +++ b/code/datums/movement/movement.dm @@ -26,9 +26,8 @@ if(LAZYLEN(movement_handlers) && ispath(movement_handlers[1])) { \ if(ispath(movement_handlers[1])) return (handler_path in movement_handlers) else - for(var/mh in movement_handlers) - var/datum/MH = mh - if(MH.type == handler_path) + for(var/datum/movement_handler/movement_handler as anything in movement_handlers) + if(movement_handler.type == handler_path) return TRUE return FALSE @@ -78,7 +77,7 @@ if(LAZYLEN(movement_handlers) && ispath(movement_handlers[1])) { \ #define SET_MOVER(X) X = X || src #define SET_IS_EXTERNAL(X) is_external = isnull(is_external) ? (mover != src) : is_external -/atom/movable/proc/DoMove(var/direction, var/mob/mover, var/is_external) +/atom/movable/proc/DoMove(direction, mob/mover, is_external) if(!direction || !isnum(direction)) return MOVEMENT_STOP @@ -97,8 +96,7 @@ if(LAZYLEN(movement_handlers) && ispath(movement_handlers[1])) { \ step(src, direction) return loc != oldloc - for(var/mh in movement_handlers) - var/datum/movement_handler/movement_handler = mh + for(var/datum/movement_handler/movement_handler as anything in movement_handlers) if(movement_handler.MayMove(mover, is_external) & MOVEMENT_STOP) return MOVEMENT_STOP @@ -110,13 +108,12 @@ if(LAZYLEN(movement_handlers) && ispath(movement_handlers[1])) { \ // is_external means that something else (not inside us) is asking if we may move // This for example includes mobs bumping into each other -/atom/movable/proc/MayMove(var/mob/mover, var/is_external) +/atom/movable/proc/MayMove(mob/mover, is_external) INIT_MOVEMENT_HANDLERS SET_MOVER(mover) SET_IS_EXTERNAL(mover) - for(var/mh in movement_handlers) - var/datum/movement_handler/movement_handler = mh + for(var/datum/movement_handler/movement_handler as anything in movement_handlers) var/may_move = movement_handler.MayMove(mover, is_external) if(may_move & MOVEMENT_STOP) return FALSE @@ -142,11 +139,11 @@ if(LAZYLEN(movement_handlers) && ispath(movement_handlers[1])) { \ host = null . = ..() -/datum/movement_handler/proc/DoMove(var/direction, var/mob/mover, var/is_external) +/datum/movement_handler/proc/DoMove(direction, mob/mover, is_external) return // Asks the handlers if the mob may move, ignoring destination, if attempting a DoMove() -/datum/movement_handler/proc/MayMove(var/mob/mover, var/is_external) +/datum/movement_handler/proc/MayMove(mob/mover, is_external) return MOVEMENT_PROCEED /******* diff --git a/code/datums/movement/multiz.dm b/code/datums/movement/multiz.dm index 15b041c5a65..77b6315c22c 100644 --- a/code/datums/movement/multiz.dm +++ b/code/datums/movement/multiz.dm @@ -1,4 +1,4 @@ -/datum/movement_handler/mob/multiz/DoMove(var/direction, var/mob/mover, var/is_external) +/datum/movement_handler/mob/multiz/DoMove(direction, mob/mover, is_external) if(!(direction & (UP|DOWN))) return MOVEMENT_PROCEED @@ -29,7 +29,7 @@ return MOVEMENT_PROCEED //For ghosts and such -/datum/movement_handler/mob/multiz_connected/DoMove(var/direction, var/mob/mover, var/is_external) +/datum/movement_handler/mob/multiz_connected/DoMove(direction, mob/mover, is_external) if(!(direction & (UP|DOWN))) return MOVEMENT_PROCEED @@ -40,7 +40,7 @@ return MOVEMENT_PROCEED -/datum/movement_handler/deny_multiz/DoMove(var/direction, var/mob/mover, var/is_external) +/datum/movement_handler/deny_multiz/DoMove(direction, mob/mover, is_external) if(direction & (UP|DOWN)) return MOVEMENT_HANDLED return MOVEMENT_PROCEED diff --git a/code/datums/movement/robot.dm b/code/datums/movement/robot.dm index 29e54c1eaf6..258c5e840b4 100644 --- a/code/datums/movement/robot.dm +++ b/code/datums/movement/robot.dm @@ -14,10 +14,10 @@ . = ..() // Use power while moving. -/datum/movement_handler/robot/use_power/DoMove() +/datum/movement_handler/robot/use_power/DoMove(direction, mob/mover, is_external) var/datum/robot_component/actuator/A = robot.get_component("actuator") if(!robot.cell_use_power(A.active_usage * robot.power_efficiency)) return MOVEMENT_HANDLED -/datum/movement_handler/robot/use_power/MayMove() +/datum/movement_handler/robot/use_power/MayMove(mob/mover, is_external) return (!robot.lockcharge && robot.is_component_functioning("actuator")) ? MOVEMENT_PROCEED : MOVEMENT_STOP diff --git a/code/datums/observation/crate_sold.dm b/code/datums/observation/crate_sold.dm new file mode 100644 index 00000000000..fb6b77ca1a3 --- /dev/null +++ b/code/datums/observation/crate_sold.dm @@ -0,0 +1,11 @@ +// Observer Pattern Implementation: Crate Sold +// Registration type: /area +// +// Raised when: A crate is sold on the shuttle. +// +// Arguments that the called proc should expect: +// /area/shuttle: The shuttle the crate was sold on. +// /obj/structure/closet/crate/sold: The crate that was sold. + +/decl/observ/crate_sold + name = "Crate Sold" diff --git a/code/datums/observation/cyborg_created.dm b/code/datums/observation/cyborg_created.dm new file mode 100644 index 00000000000..2e3a1cb9e48 --- /dev/null +++ b/code/datums/observation/cyborg_created.dm @@ -0,0 +1,10 @@ +/** + * Observer Pattern Implementation: Cyborg Created + * + * Raised when: A cyborg is created. + * + * Arguments that the called proc should expect: + * /mob/living/silicon/robot: The cyborg that was created. + */ +/decl/observ/cyborg_created + name = "Cyborg Created" diff --git a/code/datums/observation/debrain.dm b/code/datums/observation/debrain.dm new file mode 100644 index 00000000000..2880d90c599 --- /dev/null +++ b/code/datums/observation/debrain.dm @@ -0,0 +1,13 @@ +/** + * Observer Pattern Implementation: Debrained + * + * Raised when: A brainmob is created by the removal of a brain. + * + * Arguments that the called proc should expect: + * /mob/living/brainmob: The brainmob that was created. + * /obj/item/organ/internal/brain: The brain that was removed. + * /mob/living/owner: The mob the brain was formerly installed in. + */ +/decl/observ/debrain + name = "Debrained" + expected_type = /mob/living \ No newline at end of file diff --git a/code/datums/observation/employee_id.dm b/code/datums/observation/employee_id.dm new file mode 100644 index 00000000000..30fa7668acd --- /dev/null +++ b/code/datums/observation/employee_id.dm @@ -0,0 +1,22 @@ +/** + * Observer Pattern Implementation: Employee ID Reassigned + * + * Raised when: A card's assignment is changed in the ID card modification program. + * + * Arguments that the called proc should expect: + * /obj/item/card/id: The card that was reassigned. + */ +/decl/observ/employee_id_reassigned + name = "Employee ID Reassigned" + +// Observer Pattern Implementation: Employee ID Terminated +// Registration type: /obj/item/card/id +// +// Raised when: A card is terminated in the ID card modification program. +// +// Arguments that the called proc should expect: +// /area/shuttle: The shuttle the crate was sold on. +// /obj/structure/closet/crate/sold: The crate that was sold. + +/decl/observ/employee_id_terminated + name = "Employee ID Terminated" diff --git a/code/datums/observation/money_accounts.dm b/code/datums/observation/money_accounts.dm new file mode 100644 index 00000000000..23e4e2a0721 --- /dev/null +++ b/code/datums/observation/money_accounts.dm @@ -0,0 +1,22 @@ + +/** + * Observer Pattern Implementation: Payroll Revoked + * + * Raised when: Someone's payroll is stolen at the Accounts terminal. + * + * Arguments that the called proc should expect: + * /datum/money_account: The account whose payroll was revoked. + */ +/decl/observ/revoke_payroll + name = "Payroll Revoked" + +/** + * Observer Pattern Implementation: Account Status Changed + * + * Raised when: Someone's account is suspended or unsuspended at the Accounts terminal. + * + * Arguments that the called proc should expect: + * /datum/money_account: The account whose status was changed. + */ +/decl/observ/change_account_status + name = "Account Status Changed" diff --git a/code/datums/observation/player_latejoin.dm b/code/datums/observation/player_latejoin.dm new file mode 100644 index 00000000000..938f113b19c --- /dev/null +++ b/code/datums/observation/player_latejoin.dm @@ -0,0 +1,11 @@ +// Observer Pattern Implementation: Player Latejoin +// Registration type: /mob/living +// +// Raised when: A player joins the round after it has started. +// +// Arguments that the called proc should expect: +// /mob/living/character: The mob that joined the round. +// /datum/job/job: The job the mob joined as. + +/decl/observ/player_latejoin + name = "Player Latejoin" diff --git a/code/datums/observation/submap_join.dm b/code/datums/observation/submap_join.dm new file mode 100644 index 00000000000..aa74148fe9a --- /dev/null +++ b/code/datums/observation/submap_join.dm @@ -0,0 +1,13 @@ +// Observer Pattern Implementation: Submap Join +// Registration type: /datum/submap +// +// Raised when: A mob joins on a submap +// +// Arguments that the called proc should expect: +// /datum/submap/submap: The submap the mob joined. +// /mob/joiner: The mob that joined the submap. +// /datum/job/job: The job the mob joined as. + +/decl/observ/submap_join + name = "Submap Joined" + expected_type = /datum/submap \ No newline at end of file diff --git a/code/datums/supplypacks/livecargo.dm b/code/datums/supplypacks/livecargo.dm index 131e5daa102..97d51059081 100644 --- a/code/datums/supplypacks/livecargo.dm +++ b/code/datums/supplypacks/livecargo.dm @@ -33,14 +33,14 @@ /decl/hierarchy/supply_pack/livecargo/goat name = "Live - Goat" - contains = list(/mob/living/simple_animal/hostile/retaliate/goat) + contains = list(/mob/living/simple_animal/hostile/goat) containertype = /obj/structure/largecrate/animal containername = "goat crate" access = access_hydroponics /decl/hierarchy/supply_pack/livecargo/goose name = "Live - Goose" - contains = list(/mob/living/simple_animal/hostile/retaliate/goose) + contains = list(/mob/living/simple_animal/hostile/goose) containertype = /obj/structure/largecrate/animal containername = "goose containment unit" access = access_hydroponics diff --git a/code/datums/trading/traders/goods.dm b/code/datums/trading/traders/goods.dm index 6c1c4e7c443..d4a6d246556 100644 --- a/code/datums/trading/traders/goods.dm +++ b/code/datums/trading/traders/goods.dm @@ -342,9 +342,9 @@ Sells devices, odds and ends, and medical stuff /mob/living/simple_animal/tindalos = TRADER_THIS_TYPE, /mob/living/simple_animal/tomato = TRADER_THIS_TYPE, /mob/living/simple_animal/yithian = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/beast/diyaab = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/beast/shantak = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/beast/samak = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/beast/diyaab = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/beast/shantak = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/beast/samak = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/carp = TRADER_THIS_TYPE ) possible_trading_items = list( diff --git a/code/datums/trading/traders/misc.dm b/code/datums/trading/traders/misc.dm index f0d7470c817..ec351376626 100644 --- a/code/datums/trading/traders/misc.dm +++ b/code/datums/trading/traders/misc.dm @@ -29,7 +29,7 @@ possible_wanted_items = list( /mob/living/simple_animal/corgi = TRADER_THIS_TYPE, - /mob/living/simple_animal/cat = TRADER_THIS_TYPE, + /mob/living/simple_animal/passive/cat = TRADER_THIS_TYPE, /mob/living/simple_animal/crab = TRADER_THIS_TYPE, /mob/living/simple_animal/lizard = TRADER_THIS_TYPE, /mob/living/simple_animal/passive/mouse = TRADER_THIS_TYPE, @@ -41,18 +41,18 @@ /mob/living/simple_animal/fowl/chicken = TRADER_THIS_TYPE, /mob/living/simple_animal/fowl/duck = TRADER_THIS_TYPE, /mob/living/simple_animal/yithian = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/beast/diyaab = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/beast/diyaab = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/bear = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/beast/shantak = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/parrot = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/beast/samak = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/goat = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/beast/shantak = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/parrot = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/beast/samak = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/goat = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/carp = TRADER_THIS_TYPE ) possible_trading_items = list( /mob/living/simple_animal/corgi = TRADER_THIS_TYPE, - /mob/living/simple_animal/cat = TRADER_THIS_TYPE, + /mob/living/simple_animal/passive/cat = TRADER_THIS_TYPE, /mob/living/simple_animal/crab = TRADER_THIS_TYPE, /mob/living/simple_animal/lizard = TRADER_THIS_TYPE, /mob/living/simple_animal/passive/mouse = TRADER_THIS_TYPE, @@ -64,12 +64,12 @@ /mob/living/simple_animal/fowl/chicken = TRADER_THIS_TYPE, /mob/living/simple_animal/fowl/duck = TRADER_THIS_TYPE, /mob/living/simple_animal/yithian = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/beast/diyaab = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/beast/diyaab = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/bear = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/beast/shantak = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/parrot = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/beast/samak = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/goat = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/beast/shantak = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/parrot = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/beast/samak = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/goat = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/carp = TRADER_THIS_TYPE, /obj/item/dociler = TRADER_THIS_TYPE, /obj/structure/dogbed = TRADER_THIS_TYPE diff --git a/code/game/atoms_temperature.dm b/code/game/atoms_temperature.dm index a37142e70e7..90f9a780829 100644 --- a/code/game/atoms_temperature.dm +++ b/code/game/atoms_temperature.dm @@ -32,18 +32,19 @@ // Get our location temperature if possible. // Nullspace is room temperature, clearly. var/adjust_temp = T20C + var/thermal_mass_coefficient = get_thermal_mass_coefficient() if(isturf(loc)) var/turf/T = loc var/datum/gas_mixture/environment = T.return_air() - if(environment) - adjust_temp = environment.temperature + adjust_temp = environment.temperature + //scale the thermal mass coefficient so that 1atm = 1x, 0atm = 0x, 10atm = 10x + thermal_mass_coefficient *= (environment.return_pressure() / ONE_ATMOSPHERE) else if(loc) adjust_temp = loc.temperature // Determine if our temperature needs to change. var/old_temp = temperature var/diff_temp = adjust_temp - temperature - var/thermal_mass_coefficient = get_thermal_mass_coefficient() if(abs(diff_temp) >= (thermal_mass_coefficient * ATOM_TEMPERATURE_EQUILIBRIUM_THRESHOLD)) var/altered_temp = max(temperature + (thermal_mass_coefficient * diff_temp), 0) ADJUST_ATOM_TEMPERATURE(src, (diff_temp > 0) ? min(adjust_temp, altered_temp) : max(adjust_temp, altered_temp)) diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 1e7c55f96dc..a3a7e7e34ca 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -234,8 +234,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ else ai_text = stars(text) if(isanimal(M) && !M.universal_speak) - var/mob/living/simple_animal/SA = M - ai_text = DEFAULTPICK(SA.emote_speech, "...") + ai_text = DEFAULTPICK(M.ai?.emote_speech, "...") var/name_used = M.GetVoice() //This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only. var/short_links = master.get_preference_value(/datum/client_preference/ghost_follow_link_length) == PREF_SHORT @@ -245,8 +244,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ var/name_used = M.GetVoice() var/message if(isanimal(M) && !M.universal_speak) - var/mob/living/simple_animal/SA = M - message = get_hear_message(name_used, DEFAULTPICK(SA.emote_speech, "..."), verb, speaking) + message = get_hear_message(name_used, DEFAULTPICK(M.ai?.emote_speech, "..."), verb, speaking) else message = get_hear_message(name_used, text, verb, speaking) if(targetpad && !targetpad.incoming_connection) //If this is the pad you're making the call from and the call is accepted diff --git a/code/game/objects/effects/gateway.dm b/code/game/objects/effects/gateway.dm index 1323ffda6dd..3a976ecf4de 100644 --- a/code/game/objects/effects/gateway.dm +++ b/code/game/objects/effects/gateway.dm @@ -13,7 +13,7 @@ spawnable=list( /mob/living/simple_animal/hostile/scarybat, /mob/living/simple_animal/hostile/creature, - /mob/living/simple_animal/hostile/faithless + /mob/living/simple_animal/hostile/revenant ) /obj/effect/gateway/active/Initialize() diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 3ecdcf8fcb8..4c2a64e69f9 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -190,7 +190,7 @@ if(dormant) events_repository.unregister(/decl/observ/moved, src, src, TYPE_PROC_REF(/obj/effect/spider, disturbed)) STOP_PROCESSING(SSobj, src) - walk(src, 0) // Because we might have called walk_to, we must stop the walk loop or BYOND keeps an internal reference to us forever. + stop_automove() . = ..() /obj/effect/spider/spiderling/attackby(var/obj/item/W, var/mob/user) diff --git a/code/game/objects/items/blades/_blade.dm b/code/game/objects/items/blades/_blade.dm index bd6f5bc8f0e..ccde6d9ef77 100644 --- a/code/game/objects/items/blades/_blade.dm +++ b/code/game/objects/items/blades/_blade.dm @@ -65,7 +65,7 @@ ) switch(w_class) if(0 to ITEM_SIZE_SMALL) - initial_tool_qualities[TOOL_HATCHET] = TOOL_QUALITY_NONE + pass() if(ITEM_SIZE_SMALL to ITEM_SIZE_NORMAL) // Since ITEM_SIZE_SMALL was already covered, this is just ITEM_SIZE_NORMAL. initial_tool_qualities[TOOL_HATCHET] = TOOL_QUALITY_WORST if(ITEM_SIZE_NORMAL to ITEM_SIZE_LARGE) diff --git a/code/game/objects/items/devices/dociler.dm b/code/game/objects/items/devices/dociler.dm index c0f8e183dbb..003fadb14a1 100644 --- a/code/game/objects/items/devices/dociler.dm +++ b/code/game/objects/items/devices/dociler.dm @@ -40,11 +40,7 @@ target.faction = user.faction else target.faction = null - if(istype(target, /mob/living/simple_animal/hostile)) - var/mob/living/simple_animal/hostile/H = target - H.LoseTarget() - H.attack_same = 0 - H.friends += weakref(user) + target.ai?.pacify(user) target.desc += "
It looks especially docile." var/name = input(user, "Would you like to rename \the [target]?", "Dociler", target.name) as text if(length(name)) diff --git a/code/game/objects/items/flame/flame_fuelled_lantern.dm b/code/game/objects/items/flame/flame_fuelled_lantern.dm index bdcd8010c13..b911f64a09f 100644 --- a/code/game/objects/items/flame/flame_fuelled_lantern.dm +++ b/code/game/objects/items/flame/flame_fuelled_lantern.dm @@ -10,7 +10,8 @@ slot_flags = SLOT_LOWER_BODY lit_light_power = 0.7 lit_light_range = 6 - max_fuel = 120 + max_fuel = 60 + _fuel_spend_amt = (1 / 60) // a full lantern should last an hour material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC material = /decl/material/solid/metal/copper can_manually_light = FALSE diff --git a/code/game/objects/items/flashlights/_flashlight.dm b/code/game/objects/items/flashlights/_flashlight.dm index 989f8f6bc64..06397130139 100644 --- a/code/game/objects/items/flashlights/_flashlight.dm +++ b/code/game/objects/items/flashlights/_flashlight.dm @@ -97,7 +97,7 @@ /obj/item/flashlight/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) - if(on && user.get_target_zone() == BP_EYES && target.should_have_organ(BP_HEAD)) + if(on && user.get_target_zone() == BP_EYES && target.should_have_limb(BP_HEAD)) add_fingerprint(user) if(user.has_genetic_condition(GENE_COND_CLUMSY) && prob(50)) //too dumb to use flashlight properly diff --git a/code/game/objects/items/robot/robot_frame.dm b/code/game/objects/items/robot/robot_frame.dm index b47813774c0..6c6f6857f79 100644 --- a/code/game/objects/items/robot/robot_frame.dm +++ b/code/game/objects/items/robot/robot_frame.dm @@ -128,7 +128,7 @@ cell_component.installed = 1 SSstatistics.add_field("cyborg_birth",1) - callHook("borgify", list(O)) + RAISE_EVENT(/decl/observ/cyborg_created, O) O.Namepick() qdel(src) diff --git a/code/game/objects/items/waterskin.dm b/code/game/objects/items/waterskin.dm index e06e6b92737..b3364096439 100644 --- a/code/game/objects/items/waterskin.dm +++ b/code/game/objects/items/waterskin.dm @@ -4,9 +4,21 @@ icon = 'icons/obj/items/waterskin.dmi' icon_state = ICON_STATE_WORLD material = /decl/material/solid/organic/leather/gut + atom_flags = ATOM_FLAG_OPEN_CONTAINER volume = 120 material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME +/obj/item/chems/waterskin/attack_self() + . = ..() + if(!.) + if(ATOM_IS_OPEN_CONTAINER(src)) + to_chat(usr, SPAN_NOTICE("You cork \the [src].")) + atom_flags ^= ATOM_FLAG_OPEN_CONTAINER + else + to_chat(usr, SPAN_NOTICE("You remove the cork from \the [src].")) + atom_flags |= ATOM_FLAG_OPEN_CONTAINER + update_icon() // TODO: filled/empty and corked/uncorked sprites + /obj/item/chems/waterskin/crafted desc = "A long and rather unwieldly water-carrying vessel." material = /decl/material/solid/organic/leather @@ -17,4 +29,4 @@ /obj/item/chems/waterskin/crafted/wine/populate_reagents() . = ..() - add_to_reagents(/decl/material/liquid/ethanol/wine, reagents?.total_volume) + add_to_reagents(/decl/material/liquid/ethanol/wine, reagents?.maximum_volume) diff --git a/code/game/objects/items/weapons/grenades/prank_grenades.dm b/code/game/objects/items/weapons/grenades/prank_grenades.dm index 8e7c1242f39..20913343862 100644 --- a/code/game/objects/items/weapons/grenades/prank_grenades.dm +++ b/code/game/objects/items/weapons/grenades/prank_grenades.dm @@ -12,7 +12,10 @@ faction = null natural_weapon = /obj/item/natural_weapon/bite/fake environment_smash = 0 - destroy_surroundings = 0 + ai = /datum/mob_controller/aggressive/carp/fake + +/datum/mob_controller/aggressive/carp/fake + try_destroy_surroundings = FALSE /obj/item/grenade/spawnergrenade/fake_carp origin_tech = @'{"materials":2,"magnets":2,"wormholes":5}' diff --git a/code/game/objects/items/weapons/tape.dm b/code/game/objects/items/weapons/tape.dm index 8e92379b480..7a527b80a92 100644 --- a/code/game/objects/items/weapons/tape.dm +++ b/code/game/objects/items/weapons/tape.dm @@ -58,7 +58,7 @@ to_chat(user, SPAN_WARNING("\The [target] isn't wearing a spacesuit for you to reseal.")) return TRUE - if(!target?.should_have_organ(BP_HEAD)) + if(!target?.should_have_limb(BP_HEAD)) return ..() if(user.get_target_zone() == BP_EYES) diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index b1825375cde..572af911651 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -28,7 +28,7 @@ user.take_organ_damage(10) SET_STATUS_MAX(user, STAT_PARA, 20) return TRUE - + if (holy_act(target, user)) return TRUE @@ -219,6 +219,7 @@ /obj/effect/energy_net/proc/escape_net(mob/user) + set waitfor = FALSE visible_message( "\The [user] attempts to free themselves from \the [src]!", "You attempt to free yourself from \the [src]!" @@ -227,5 +228,4 @@ current_health = 0 healthcheck() return 1 - else - return 0 + return 0 diff --git a/code/game/objects/structures/compost.dm b/code/game/objects/structures/compost.dm index 18ef1e5faf9..a010130a330 100644 --- a/code/game/objects/structures/compost.dm +++ b/code/game/objects/structures/compost.dm @@ -199,3 +199,11 @@ var/global/const/COMPOST_WORM_HUNGER_FACTOR = MINIMUM_CHEMICAL_VOLUME /obj/structure/reagent_dispensers/compost_bin/get_alt_interactions(var/mob/user) . = ..() LAZYREMOVE(., /decl/interaction_handler/toggle_open/reagent_dispenser) + +/obj/structure/reagent_dispensers/compost_bin/ebony + material = /decl/material/solid/organic/wood/ebony + color = /decl/material/solid/organic/wood/ebony::color + +/obj/structure/reagent_dispensers/compost_bin/walnut + material = /decl/material/solid/organic/wood/walnut + color = /decl/material/solid/organic/wood/walnut::color \ No newline at end of file diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index 860d8889bea..5c1e42f3764 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -42,7 +42,7 @@ name = "[name] ([critter.name])" /obj/structure/largecrate/animal/cat - animal_type = /mob/living/simple_animal/cat + animal_type = /mob/living/simple_animal/passive/cat /obj/structure/largecrate/animal/cow animal_type = /mob/living/simple_animal/cow diff --git a/code/game/objects/structures/doors/_door.dm b/code/game/objects/structures/doors/_door.dm index c590d90dee4..ff3832655c8 100644 --- a/code/game/objects/structures/doors/_door.dm +++ b/code/game/objects/structures/doors/_door.dm @@ -201,35 +201,32 @@ /obj/structure/door/wood material = /decl/material/solid/organic/wood + color = /decl/material/solid/organic/wood::color /obj/structure/door/mahogany material = /decl/material/solid/organic/wood/mahogany + color = /decl/material/solid/organic/wood/mahogany::color /obj/structure/door/maple material = /decl/material/solid/organic/wood/maple + color = /decl/material/solid/organic/wood/maple::color /obj/structure/door/ebony material = /decl/material/solid/organic/wood/ebony + color = /decl/material/solid/organic/wood/ebony::color /obj/structure/door/walnut material = /decl/material/solid/organic/wood/walnut + color = /decl/material/solid/organic/wood/walnut::color /obj/structure/door/wood/saloon material = /decl/material/solid/organic/wood opacity = FALSE -/obj/structure/door/wood/ebony - material = /decl/material/solid/organic/wood/ebony - color = /decl/material/solid/organic/wood/ebony::color - /obj/structure/door/wood/saloon/ebony material = /decl/material/solid/organic/wood/ebony color = /decl/material/solid/organic/wood/ebony::color -/obj/structure/door/wood/walnut - material = /decl/material/solid/organic/wood/walnut - color = /decl/material/solid/organic/wood/walnut::color - /obj/structure/door/wood/saloon/walnut material = /decl/material/solid/organic/wood/walnut color = /decl/material/solid/organic/wood/walnut::color diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index 76b4b96fbe9..0910172cc39 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -27,12 +27,29 @@ /obj/structure/railing/mapped/no_density density = FALSE -/obj/structure/railing/mapped/ebony - material = /decl/material/solid/organic/wood/ebony +/obj/structure/railing/mapped/wooden + material = /decl/material/solid/organic/wood parts_type = /obj/item/stack/material/plank - color = WOOD_COLOR_BLACK + color = WOOD_COLOR_GENERIC paint_color = null +// Subtypes. +#define WOOD_RAILING_SUBTYPE(material_name) \ +/obj/structure/railing/mapped/wooden/##material_name { \ + material = /decl/material/solid/organic/wood/##material_name; \ + color = /decl/material/solid/organic/wood/##material_name::color; \ +} + +WOOD_RAILING_SUBTYPE(fungal) +WOOD_RAILING_SUBTYPE(ebony) +WOOD_RAILING_SUBTYPE(walnut) +WOOD_RAILING_SUBTYPE(maple) +WOOD_RAILING_SUBTYPE(mahogany) +WOOD_RAILING_SUBTYPE(bamboo) +WOOD_RAILING_SUBTYPE(yew) + +#undef WOOD_RAILING_SUBTYPE + /obj/structure/railing/Process() if(!material || !material.radioactivity) return diff --git a/code/game/objects/structures/wall_sconce.dm b/code/game/objects/structures/wall_sconce.dm index 25790c2df1c..8a1323e989d 100644 --- a/code/game/objects/structures/wall_sconce.dm +++ b/code/game/objects/structures/wall_sconce.dm @@ -31,11 +31,14 @@ directional_offset = @'{"NORTH":{"y":24}, "SOUTH":{"y":-1}, "EAST":{"x":10,"y":10}, "WEST":{"x":-10,"y":10}}' /// Reference to the currently attached item. var/obj/item/flame/light_source + /// Whether or not the light source, if present, is automatically lit on Initialize. + var/start_lit = FALSE /obj/structure/wall_sconce/Initialize(var/ml, var/_mat, var/_reinf_mat, var/supplied_dir) if(ispath(light_source)) light_source = new light_source(src) + if(start_lit && istype(light_source)) light_source.light(null, no_message = TRUE) . = ..() diff --git a/code/game/world.dm b/code/game/world.dm index f5b8dc6d4a7..420c09757f5 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -121,11 +121,13 @@ var/global/world_topic_last = world.timeofday if(!length(params)) return var/command_key = params[1] - if(!command_key || !global.topic_commands[command_key]) + if(!command_key) + return "Unrecognised Command" + var/decl/topic_command/command = decls_repository.get_decl_by_id("topic_command_[command_key]") + if(!istype(command)) return "Unrecognised Command" - var/decl/topic_command/TC = global.topic_commands[command_key] - return TC.try_use(T, addr, master, key) + return command.try_use(T, addr, master, key) /world/Reboot(var/reason) diff --git a/code/game/world_topic_commands.dm b/code/game/world_topic_commands.dm index 78a4319710c..2bbc05f1436 100644 --- a/code/game/world_topic_commands.dm +++ b/code/game/world_topic_commands.dm @@ -1,24 +1,10 @@ -var/global/list/decl/topic_command/topic_commands = list() - /decl/topic_command + abstract_type = /decl/topic_command var/name var/has_params = FALSE - var/base_type = /decl/topic_command - -/// Initialises the assoc list of topic commands by key. -/hook/startup/proc/setup_api() - var/list/commands = decls_repository.get_decls_of_subtype(/decl/topic_command) - for (var/command in commands) - var/decl/topic_command/TC = commands[command] - if(TC.base_type == command) - continue - global.topic_commands[TC.name] = TC - return TRUE /// Returns TRUE if we can use this command, and FALSE otherwise /decl/topic_command/proc/can_use(var/T, var/addr, var/master, var/key) - if(base_type == type) // this is an abstract type - return FALSE if (has_params) if (copytext(T, 1, length(name) + 1) != name) return FALSE @@ -38,7 +24,7 @@ var/global/list/decl/topic_command/topic_commands = list() return use(params) /decl/topic_command/secure - base_type = /decl/topic_command/secure + abstract_type = /decl/topic_command/secure /decl/topic_command/secure/try_use(var/T, var/addr, var/master, var/key) if (!can_use(T, addr, master, key)) @@ -60,6 +46,7 @@ var/global/list/decl/topic_command/topic_commands = list() /decl/topic_command/ping name = "ping" + uid = "topic_command_ping" /decl/topic_command/ping/use() var/x = 1 @@ -69,12 +56,14 @@ var/global/list/decl/topic_command/topic_commands = list() /decl/topic_command/players name = "players" + uid = "topic_command_players" /decl/topic_command/players/use() return global.clients.len /decl/topic_command/status name = "status" + uid = "topic_command_status" has_params = TRUE /decl/topic_command/status/use(var/list/params) @@ -119,6 +108,7 @@ var/global/list/decl/topic_command/topic_commands = list() /decl/topic_command/manifest name = "manifest" + uid = "topic_command_manifest" /decl/topic_command/manifest/use() var/list/positions = list() @@ -138,6 +128,7 @@ var/global/list/decl/topic_command/topic_commands = list() /decl/topic_command/revision name = "revision" + uid = "topic_command_revision" /decl/topic_command/revision/use() var/list/L = list() @@ -162,6 +153,7 @@ var/global/list/decl/topic_command/topic_commands = list() * * * * * * * */ /decl/topic_command/ban name = "placepermaban" + uid = "topic_command_placepermaban" has_params = TRUE /decl/topic_command/ban/try_use(var/T, var/addr, var/master, var/key) @@ -205,6 +197,7 @@ var/global/list/decl/topic_command/topic_commands = list() /decl/topic_command/secure/laws name = "laws" + uid = "topic_command_laws" has_params = TRUE /decl/topic_command/secure/laws/use(var/list/params) @@ -251,6 +244,7 @@ var/global/list/decl/topic_command/topic_commands = list() /decl/topic_command/secure/info name = "info" + uid = "topic_command_info" has_params = TRUE /decl/topic_command/secure/info/use(var/list/params) @@ -300,6 +294,7 @@ var/global/list/decl/topic_command/topic_commands = list() /decl/topic_command/secure/adminmsg name = "adminmsg" + uid = "topic_command_adminmsg" has_params = TRUE /decl/topic_command/secure/adminmsg/use(var/list/params) @@ -335,6 +330,7 @@ var/global/list/decl/topic_command/topic_commands = list() /decl/topic_command/secure/notes name = "notes" + uid = "topic_command_notes" has_params = TRUE /decl/topic_command/secure/notes/use(var/list/params) @@ -342,6 +338,7 @@ var/global/list/decl/topic_command/topic_commands = list() /decl/topic_command/secure/age name = "age" + uid = "topic_command_age" has_params = TRUE /decl/topic_command/secure/age/use(var/list/params) @@ -356,6 +353,7 @@ var/global/list/decl/topic_command/topic_commands = list() /decl/topic_command/secure/prometheus_metrics name = "prometheus_metrics" + uid = "topic_command_prometheus_metrics" /decl/topic_command/secure/prometheus_metrics/use() if(!global.prometheus_metrics) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 000078e5a79..42aa0a029b9 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -961,24 +961,24 @@ var/global/BSACooldown = 0 /proc/is_special_character(var/character) // returns 1 for special characters and 2 for heroes of gamemode if(!SSticker.mode) return 0 - var/datum/mind/M + var/datum/mind/mind if (ismob(character)) - var/mob/C = character - M = C.mind + var/mob/character_mob = character + mind = character_mob.mind else if(istype(character, /datum/mind)) - M = character + mind = character - if(M) + if(mind) if(SSticker.mode.antag_templates && SSticker.mode.antag_templates.len) for(var/decl/special_role/antag in SSticker.mode.antag_templates) - if(antag.is_antagonist(M)) + if(antag.is_antagonist(mind)) return 2 - if(M.assigned_special_role) + if(mind.assigned_special_role) return 1 if(isrobot(character)) - var/mob/living/silicon/robot/R = character - if(R.emagged) + var/mob/living/silicon/robot/robot = character + if(robot.emagged) return 1 return 0 diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index 63188f399eb..0170ec02871 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -10,255 +10,255 @@ else if(href_list["rename"]) if(!check_rights(R_VAREDIT)) return - var/mob/M = locate(href_list["rename"]) - if(!istype(M)) + var/mob/victim = locate(href_list["rename"]) + if(!istype(victim)) to_chat(usr, "This can only be used on instances of type /mob") return - var/new_name = sanitize(input(usr,"What would you like to name this mob?","Input a name",M.real_name) as text|null, MAX_NAME_LEN) - if(!new_name || !M) return + var/new_name = sanitize(input(usr,"What would you like to name this mob?","Input a name",victim.real_name) as text|null, MAX_NAME_LEN) + if(!new_name || !victim) return - message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].") - M.fully_replace_character_name(new_name) + message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(victim)] to [new_name].") + victim.fully_replace_character_name(new_name) href_list["datumrefresh"] = href_list["rename"] else if(href_list["dressup"]) if(!check_rights(R_VAREDIT)) return - var/mob/living/human/H = locate(href_list["dressup"]) - if(!istype(H)) + var/mob/living/human/victim = locate(href_list["dressup"]) + if(!istype(victim)) to_chat(usr, "This can only be used on instances of type /mob/living/human") return var/decl/hierarchy/outfit/outfit = input("Select outfit.", "Select equipment.") as null|anything in outfits() if(!outfit) return - dressup_human(H, outfit, TRUE) + dressup_human(victim, outfit, TRUE) else if(href_list["varnameedit"] && href_list["datumedit"]) if(!check_rights(R_VAREDIT)) return - var/D = locate(href_list["datumedit"]) - if(!istype(D,/datum) && !istype(D,/client)) + var/datum_to_edit = locate(href_list["datumedit"]) + if(!istype(datum_to_edit, /datum) && !istype(datum_to_edit, /client)) to_chat(usr, "This can only be used on instances of types /client or /datum") return - modify_variables(D, href_list["varnameedit"], 1) + modify_variables(datum_to_edit, href_list["varnameedit"], 1) else if(href_list["varnamechange"] && href_list["datumchange"]) if(!check_rights(R_VAREDIT)) return - var/D = locate(href_list["datumchange"]) - if(!istype(D,/datum) && !istype(D,/client)) + var/datum_to_edit = locate(href_list["datumchange"]) + if(!istype(datum_to_edit,/datum) && !istype(datum_to_edit,/client)) to_chat(usr, "This can only be used on instances of types /client or /datum") return - modify_variables(D, href_list["varnamechange"], 0) + modify_variables(datum_to_edit, href_list["varnamechange"], 0) else if(href_list["varnamemass"] && href_list["datummass"]) if(!check_rights(R_VAREDIT)) return - var/atom/A = locate(href_list["datummass"]) - if(!istype(A)) + var/atom/atom_to_edit = locate(href_list["datummass"]) + if(!isatom(atom_to_edit)) to_chat(usr, "This can only be used on instances of type /atom") return - cmd_mass_modify_object_variables(A, href_list["varnamemass"]) + cmd_mass_modify_object_variables(atom_to_edit, href_list["varnamemass"]) else if(href_list["datumwatch"] && href_list["varnamewatch"]) - var/datum/D = locate(href_list["datumwatch"]) - if(D) - if(!watched_variables[D]) - watched_variables[D] = list() - watched_variables[D] |= href_list["varnamewatch"] + var/datum/datum_to_edit = locate(href_list["datumwatch"]) + if(datum_to_edit) + if(!watched_variables[datum_to_edit]) + watched_variables[datum_to_edit] = list() + watched_variables[datum_to_edit] |= href_list["varnamewatch"] watched_variables() if(!watched_variables_window.is_processing) START_PROCESSING(SSprocessing, watched_variables_window) else if(href_list["datumunwatch"] && href_list["varnameunwatch"]) - var/datum/D = locate(href_list["datumunwatch"]) - if(D && watched_variables[D]) - watched_variables[D] -= href_list["varnameunwatch"] - var/list/datums_watched_vars = watched_variables[D] + var/datum/datum_to_edit = locate(href_list["datumunwatch"]) + if(datum_to_edit && watched_variables[datum_to_edit]) + watched_variables[datum_to_edit] -= href_list["varnameunwatch"] + var/list/datums_watched_vars = watched_variables[datum_to_edit] if(!datums_watched_vars.len) - watched_variables -= D + watched_variables -= datum_to_edit if(!watched_variables.len && watched_variables_window.is_processing) STOP_PROCESSING(SSprocessing, watched_variables_window) else if(href_list["mob_player_panel"]) if(!check_rights(0)) return - var/mob/M = locate(href_list["mob_player_panel"]) - if(!istype(M)) + var/mob/victim = locate(href_list["mob_player_panel"]) + if(!istype(victim)) to_chat(usr, "This can only be used on instances of type /mob") return - src.holder.show_player_panel(M) + src.holder.show_player_panel(victim) href_list["datumrefresh"] = href_list["mob_player_panel"] else if(href_list["give_spell"]) if(!check_rights(R_ADMIN|R_FUN)) return - var/mob/M = locate(href_list["give_spell"]) - if(!istype(M)) + var/mob/victim = locate(href_list["give_spell"]) + if(!istype(victim)) to_chat(usr, "This can only be used on instances of type /mob") return - src.give_spell(M) + src.give_spell(victim) href_list["datumrefresh"] = href_list["give_spell"] else if(href_list["godmode"]) if(!check_rights(R_REJUVENATE)) return - var/mob/M = locate(href_list["godmode"]) - if(!istype(M)) + var/mob/victim = locate(href_list["godmode"]) + if(!istype(victim)) to_chat(usr, "This can only be used on instances of type /mob") return - src.cmd_admin_godmode(M) + src.cmd_admin_godmode(victim) href_list["datumrefresh"] = href_list["godmode"] else if(href_list["gib"]) if(!check_rights(0)) return - var/mob/M = locate(href_list["gib"]) - if(!istype(M)) + var/mob/victim = locate(href_list["gib"]) + if(!istype(victim)) to_chat(usr, "This can only be used on instances of type /mob") return - src.cmd_admin_gib(M) + src.cmd_admin_gib(victim) else if(href_list["drop_everything"]) if(!check_rights(R_DEBUG|R_ADMIN)) return - var/mob/M = locate(href_list["drop_everything"]) - if(!istype(M)) + var/mob/victim = locate(href_list["drop_everything"]) + if(!istype(victim)) to_chat(usr, "This can only be used on instances of type /mob") return if(usr.client) - usr.client.cmd_admin_drop_everything(M) + usr.client.cmd_admin_drop_everything(victim) else if(href_list["direct_control"]) if(!check_rights(0)) return - var/mob/M = locate(href_list["direct_control"]) - if(!istype(M)) + var/mob/victim = locate(href_list["direct_control"]) + if(!istype(victim)) to_chat(usr, "This can only be used on instances of type /mob") return if(usr.client) - usr.client.cmd_assume_direct_control(M) + usr.client.cmd_assume_direct_control(victim) else if(href_list["delthis"]) if(!check_rights(R_DEBUG|R_SERVER)) return - var/obj/O = locate(href_list["delthis"]) - if(!isobj(O)) + var/obj/object = locate(href_list["delthis"]) + if(!isobj(object)) to_chat(usr, "This can only be used on instances of type /obj") return - cmd_admin_delete(O) + cmd_admin_delete(object) else if(href_list["delall"]) if(!check_rights(R_DEBUG|R_SERVER)) return - var/obj/O = locate(href_list["delall"]) - if(!isobj(O)) + var/obj/object = locate(href_list["delall"]) + if(!isobj(object)) to_chat(usr, "This can only be used on instances of type /obj") return - var/action_type = alert("Strict type ([O.type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel") + var/action_type = alert("Strict type ([object.type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel") if(action_type == "Cancel" || !action_type) return - if(alert("Are you really sure you want to delete all objects of type [O.type]?",,"Yes","No") != "Yes") + if(alert("Are you really sure you want to delete all objects of type [object.type]?",,"Yes","No") != "Yes") return if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes") return - var/O_type = O.type + var/type_to_delete = object.type switch(action_type) if("Strict type") - var/i = 0 - for(var/obj/Obj in world) - if(Obj.type == O_type) - i++ - qdel(Obj) - if(!i) + var/count = 0 + for(var/obj/deleted_object in world) + if(deleted_object.type == type_to_delete) + count++ + qdel(deleted_object) + if(!count) to_chat(usr, "No objects of this type exist") return - log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted)") - message_admins("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted)") + log_admin("[key_name(usr)] deleted all objects of type [type_to_delete] ([count] objects deleted)") + message_admins("[key_name(usr)] deleted all objects of type [type_to_delete] ([count] objects deleted)") if("Type and subtypes") - var/i = 0 - for(var/obj/Obj in world) - if(istype(Obj,O_type)) - i++ - qdel(Obj) - if(!i) + var/count = 0 + for(var/obj/deleted_object in world) + if(istype(deleted_object, type_to_delete)) + count++ + qdel(deleted_object) + if(!count) to_chat(usr, "No objects of this type exist") return - log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted)") - message_admins("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted)") + log_admin("[key_name(usr)] deleted all objects of type or subtype of [type_to_delete] ([count] objects deleted)") + message_admins("[key_name(usr)] deleted all objects of type or subtype of [type_to_delete] ([count] objects deleted)") else if(href_list["explode"]) if(!check_rights(R_DEBUG|R_FUN)) return - var/atom/A = locate(href_list["explode"]) - if(!isobj(A) && !ismob(A) && !isturf(A)) + var/atom/atom_to_explode = locate(href_list["explode"]) + if(!isobj(atom_to_explode) && !ismob(atom_to_explode) && !isturf(atom_to_explode)) to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") return - src.cmd_admin_explosion(A) + src.cmd_admin_explosion(atom_to_explode) href_list["datumrefresh"] = href_list["explode"] else if(href_list["emp"]) if(!check_rights(R_DEBUG|R_FUN)) return - var/atom/A = locate(href_list["emp"]) - if(!isobj(A) && !ismob(A) && !isturf(A)) + var/atom/atom_to_emp = locate(href_list["emp"]) + if(!isobj(atom_to_emp) && !ismob(atom_to_emp) && !isturf(atom_to_emp)) to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") return - src.cmd_admin_emp(A) + src.cmd_admin_emp(atom_to_emp) href_list["datumrefresh"] = href_list["emp"] else if(href_list["mark_object"]) if(!check_rights(0)) return - var/datum/D = locate(href_list["mark_object"]) - if(!istype(D)) + var/datum/datum_to_mark = locate(href_list["mark_object"]) + if(!istype(datum_to_mark)) to_chat(usr, "This can only be done to instances of type /datum") return - src.holder.marked_datum_weak = weakref(D) + src.holder.marked_datum_weak = weakref(datum_to_mark) href_list["datumrefresh"] = href_list["mark_object"] else if(href_list["rotatedatum"]) if(!check_rights(0)) return - var/atom/A = locate(href_list["rotatedatum"]) - if(!istype(A)) + var/atom/atom_to_rotate = locate(href_list["rotatedatum"]) + if(!isatom(atom_to_rotate)) to_chat(usr, "This can only be done to instances of type /atom") return switch(href_list["rotatedir"]) - if("right") A.set_dir(turn(A.dir, -45)) - if("left") A.set_dir(turn(A.dir, 45)) + if("right") atom_to_rotate.set_dir(turn(atom_to_rotate.dir, -45)) + if("left") atom_to_rotate.set_dir(turn(atom_to_rotate.dir, 45)) href_list["datumrefresh"] = href_list["rotatedatum"] else if(href_list["makemonkey"]) if(!check_rights(R_SPAWN)) return - var/mob/living/human/H = locate(href_list["makemonkey"]) - if(!istype(H)) + var/mob/living/human/victim = locate(href_list["makemonkey"]) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob/living/human") return if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) + if(!victim) to_chat(usr, "Mob doesn't exist anymore") return holder.Topic(href, list("monkeyone"=href_list["makemonkey"])) @@ -266,13 +266,13 @@ else if(href_list["makerobot"]) if(!check_rights(R_SPAWN)) return - var/mob/living/human/H = locate(href_list["makerobot"]) - if(!istype(H)) + var/mob/living/human/victim = locate(href_list["makerobot"]) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob/living/human") return if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) + if(!victim) to_chat(usr, "Mob doesn't exist anymore") return holder.Topic(href, list("makerobot"=href_list["makerobot"])) @@ -280,114 +280,114 @@ else if(href_list["makeai"]) if(!check_rights(R_SPAWN)) return - var/mob/living/human/H = locate(href_list["makeai"]) - if(!istype(H)) + var/mob/living/human/victim = locate(href_list["makeai"]) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob/living/human") return if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) + if(!victim) to_chat(usr, "Mob doesn't exist anymore") return holder.Topic(href, list("makeai"=href_list["makeai"])) else if(href_list["addailment"]) - var/mob/living/human/H = locate(href_list["addailment"]) - if(!istype(H)) + var/mob/living/human/victim = locate(href_list["addailment"]) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob/living/human") return - var/obj/item/organ/O = input("Select a limb to add the ailment to.", "Add Ailment") as null|anything in H.get_organs() - if(QDELETED(H) || QDELETED(O) || O.owner != H) + var/obj/item/organ/limb = input("Select a limb to add the ailment to.", "Add Ailment") as null|anything in victim.get_organs() + if(QDELETED(victim) || QDELETED(limb) || limb.owner != victim) return var/list/possible_ailments = list() for(var/atype in subtypesof(/datum/ailment)) var/datum/ailment/ailment = get_ailment_reference(atype) - if(ailment && ailment.category != ailment.type && ailment.can_apply_to(O)) + if(ailment && ailment.category != ailment.type && ailment.can_apply_to(limb)) possible_ailments |= ailment var/datum/ailment/ailment = input("Select an ailment type to add.", "Add Ailment") as null|anything in possible_ailments if(!istype(ailment)) return - if(!QDELETED(H) && !QDELETED(O) && O.owner == H && O.add_ailment(ailment)) - to_chat(usr, SPAN_NOTICE("Added [ailment] to \the [H].")) + if(!QDELETED(victim) && !QDELETED(limb) && limb.owner == victim && limb.add_ailment(ailment)) + to_chat(usr, SPAN_NOTICE("Added [ailment] to \the [victim].")) else - to_chat(usr, SPAN_WARNING("Failed to add [ailment] to \the [H].")) + to_chat(usr, SPAN_WARNING("Failed to add [ailment] to \the [victim].")) return else if(href_list["remailment"]) - var/mob/living/human/H = locate(href_list["remailment"]) - if(!istype(H)) + var/mob/living/human/victim = locate(href_list["remailment"]) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob/living/human") return var/list/all_ailments = list() - for(var/obj/item/organ/O in H.get_organs()) - for(var/datum/ailment/ailment in O.ailments) - all_ailments["[ailment.name] - [O.name]"] = ailment + for(var/obj/item/organ/limb in victim.get_organs()) + for(var/datum/ailment/ailment in limb.ailments) + all_ailments["[ailment.name] - [limb.name]"] = ailment var/datum/ailment/ailment = input("Which ailment do you wish to remove?", "Removing Ailment") as null|anything in all_ailments if(!ailment) return ailment = all_ailments[ailment] - if(istype(ailment) && ailment.organ && ailment.organ.owner == H && ailment.organ.remove_ailment(ailment)) - to_chat(usr, SPAN_NOTICE("Removed [ailment] from \the [H].")) + if(istype(ailment) && ailment.organ && ailment.organ.owner == victim && ailment.organ.remove_ailment(ailment)) + to_chat(usr, SPAN_NOTICE("Removed [ailment] from \the [victim].")) else - to_chat(usr, SPAN_WARNING("Failed to remove [ailment] from \the [H].")) + to_chat(usr, SPAN_WARNING("Failed to remove [ailment] from \the [victim].")) return else if(href_list["setspecies"]) if(!check_rights(R_SPAWN)) return - var/mob/living/human/H = locate(href_list["setspecies"]) - if(!istype(H)) + var/mob/living/human/victim = locate(href_list["setspecies"]) + if(!istype(victim)) to_chat(usr, SPAN_WARNING("This can only be done to instances of type /mob/living/human")) return var/new_species = input("Please choose a new species.","Species",null) as null|anything in get_all_species() - if(!H) + if(!victim) to_chat(usr, SPAN_WARNING("Mob doesn't exist anymore")) return if(!new_species) return - if(H.change_species(new_species)) - to_chat(usr, SPAN_NOTICE("Set species of [H] to [H.species].")) + if(victim.change_species(new_species)) + to_chat(usr, SPAN_NOTICE("Set species of [victim] to [victim.species].")) else to_chat(usr, SPAN_WARNING("Failed! Something went wrong.")) else if(href_list["setbodytype"]) if(!check_rights(R_SPAWN)) return - var/mob/living/human/H = locate(href_list["setbodytype"]) - if(!istype(H)) + var/mob/living/human/victim = locate(href_list["setbodytype"]) + if(!istype(victim)) to_chat(usr, SPAN_WARNING("This can only be done to instances of type /mob/living/human")) return - var/new_bodytype = input("Please choose a new bodytype.","Bodytype",null) as null|anything in H.species.available_bodytypes + var/new_bodytype = input("Please choose a new bodytype.","Bodytype",null) as null|anything in victim.species.available_bodytypes if(!new_bodytype) return - if(!H) + if(!victim) to_chat(usr, SPAN_WARNING("Mob doesn't exist anymore")) return - if(!(new_bodytype in H.species.available_bodytypes)) + if(!(new_bodytype in victim.species.available_bodytypes)) to_chat(usr, SPAN_WARNING("Bodytype is no longer available to the mob species.")) - if(H.set_bodytype(new_bodytype)) - to_chat(usr, SPAN_NOTICE("Set bodytype of [H] to [H.get_bodytype()].")) + if(victim.set_bodytype(new_bodytype)) + to_chat(usr, SPAN_NOTICE("Set bodytype of [victim] to [victim.get_bodytype()].")) else to_chat(usr, SPAN_WARNING("Failed! Something went wrong.")) else if(href_list["addlanguage"]) if(!check_rights(R_SPAWN)) return - var/mob/H = locate(href_list["addlanguage"]) - if(!istype(H)) + var/mob/victim = locate(href_list["addlanguage"]) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob") return @@ -397,238 +397,237 @@ if(!new_language) return - if(!H) + if(!victim) to_chat(usr, "Mob doesn't exist anymore") return - if(H.add_language(new_language)) - to_chat(usr, "Added [new_language] to [H].") + if(victim.add_language(new_language)) + to_chat(usr, "Added [new_language] to [victim].") else to_chat(usr, "Mob already knows that language.") else if(href_list["remlanguage"]) if(!check_rights(R_SPAWN)) return - var/mob/H = locate(href_list["remlanguage"]) - if(!istype(H)) + var/mob/victim = locate(href_list["remlanguage"]) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob") return - if(!H.languages.len) + if(!victim.languages.len) to_chat(usr, "This mob knows no languages.") return - var/decl/language/rem_language = input("Please choose a language to remove.","Language",null) as null|anything in H.languages + var/decl/language/rem_language = input("Please choose a language to remove.","Language",null) as null|anything in victim.languages if(!rem_language) return - if(!H) + if(!victim) to_chat(usr, "Mob doesn't exist anymore") return - if(H.remove_language(rem_language.name)) - to_chat(usr, "Removed [rem_language] from [H].") + if(victim.remove_language(rem_language.name)) + to_chat(usr, "Removed [rem_language] from [victim].") else to_chat(usr, "Mob doesn't know that language.") else if(href_list["addverb"]) if(!check_rights(R_DEBUG)) return - var/mob/living/H = locate(href_list["addverb"]) + var/mob/living/victim = locate(href_list["addverb"]) - if(!istype(H)) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob/living") return var/list/possibleverbs = list() possibleverbs += "Cancel" // One for the top... possibleverbs += typesof(/mob/proc, /mob/verb, /mob/living/proc, /mob/living/verb) - switch(H.type) - if(/mob/living/human) - possibleverbs += typesof(/mob/living/human/verb, /mob/living/human/proc) - if(/mob/living/silicon/robot) - possibleverbs += typesof(/mob/living/silicon/proc, /mob/living/silicon/robot/proc, /mob/living/silicon/robot/verb) - if(/mob/living/silicon/ai) - possibleverbs += typesof(/mob/living/silicon/proc, /mob/living/silicon/ai/proc, /mob/living/silicon/ai/verb) - possibleverbs -= H.verbs + if(ishuman(victim)) + possibleverbs += typesof(/mob/living/human/verb, /mob/living/human/proc) + else if(isrobot(victim)) + possibleverbs += typesof(/mob/living/silicon/proc, /mob/living/silicon/robot/proc, /mob/living/silicon/robot/verb) + else if(isAI(victim)) + possibleverbs += typesof(/mob/living/silicon/proc, /mob/living/silicon/ai/proc, /mob/living/silicon/ai/verb) + possibleverbs -= victim.verbs possibleverbs += "Cancel" // ...And one for the bottom var/verb = input("Select a verb!", "Verbs",null) as null|anything in possibleverbs - if(!H) + if(!victim) to_chat(usr, "Mob doesn't exist anymore") return if(!verb || verb == "Cancel") return else - H.verbs += verb + victim.verbs += verb else if(href_list["remverb"]) if(!check_rights(R_DEBUG)) return - var/mob/H = locate(href_list["remverb"]) + var/mob/victim = locate(href_list["remverb"]) - if(!istype(H)) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob") return - var/verb = input("Please choose a verb to remove.","Verbs",null) as null|anything in H.verbs - if(!H) + var/verb = input("Please choose a verb to remove.","Verbs",null) as null|anything in victim.verbs + if(!victim) to_chat(usr, "Mob doesn't exist anymore") return if(!verb) return else - H.verbs -= verb + victim.verbs -= verb else if(href_list["addorgan"]) if(!check_rights(R_SPAWN)) return - var/mob/living/M = locate(href_list["addorgan"]) - if(!istype(M)) + var/mob/living/victim = locate(href_list["addorgan"]) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob/living") return - if(!length(M.get_external_organs())) // quick and dirty check for organs; should always be >0 for humanlike mobs. + if(!length(victim.get_external_organs())) // quick and dirty check for organs; should always be >0 for humanlike mobs. to_chat(usr, "This can only be done to mobs that implement organs!") return var/obj/item/organ/new_organ = input("Please choose an organ to add.","Organ",null) as null|anything in subtypesof(/obj/item/organ) if(!new_organ) return - if(!M) + if(!victim) to_chat(usr, "Mob doesn't exist anymore") return - if(locate(new_organ) in M.get_internal_organs()) + if(locate(new_organ) in victim.get_internal_organs()) to_chat(usr, "Mob already has that organ.") return - var/obj/item/organ/target_organ = M.get_organ(initial(new_organ.parent_organ)) + var/obj/item/organ/target_organ = victim.get_organ(initial(new_organ.parent_organ)) if(!target_organ) to_chat(usr, "Mob doesn't have \a [target_organ] to install that in.") return - var/obj/item/organ/organ_instance = new new_organ(M) - M.add_organ(organ_instance, target_organ) + var/obj/item/organ/organ_instance = new new_organ(victim) + victim.add_organ(organ_instance, target_organ) else if(href_list["remorgan"]) if(!check_rights(R_SPAWN)) return - var/mob/living/M = locate(href_list["remorgan"]) - if(!istype(M)) + var/mob/living/victim = locate(href_list["remorgan"]) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob/living") return - if(!length(M.get_external_organs())) // quick and dirty check for organs; should always be >0 for humanlike mobs. + if(!length(victim.get_external_organs())) // quick and dirty check for organs; should always be >0 for humanlike mobs. to_chat(usr, "This can only be done to mobs that implement organs!") return - var/obj/item/organ/rem_organ = input("Please choose an organ to remove.","Organ",null) as null|anything in M.get_internal_organs() + var/obj/item/organ/rem_organ = input("Please choose an organ to remove.","Organ",null) as null|anything in victim.get_internal_organs() - if(!M) + if(!victim) to_chat(usr, "Mob doesn't exist anymore") return - if(!(locate(rem_organ) in M.get_internal_organs())) + if(!(locate(rem_organ) in victim.get_internal_organs())) to_chat(usr, "Mob does not have that organ.") return - to_chat(usr, "Removed [rem_organ] from [M].") - M.remove_organ(rem_organ) + to_chat(usr, "Removed [rem_organ] from [victim].") + victim.remove_organ(rem_organ) if(!QDELETED(rem_organ)) qdel(rem_organ) else if(href_list["fix_nano"]) if(!check_rights(R_DEBUG)) return - var/mob/H = locate(href_list["fix_nano"]) + var/mob/victim = locate(href_list["fix_nano"]) - if(!istype(H) || !H.client) + if(!istype(victim) || !victim.client) to_chat(usr, "This can only be done on mobs with clients") return - SSnano.close_uis(H) - H.client.cache.Cut() + SSnano.close_uis(victim) + victim.client.cache.Cut() var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui) - assets.send(H) + assets.send(victim) to_chat(usr, "Resource files sent") - to_chat(H, "Your NanoUI Resource files have been refreshed") + to_chat(victim, "Your NanoUI Resource files have been refreshed") - log_admin("[key_name(usr)] resent the NanoUI resource files to [key_name(H)] ") + log_admin("[key_name(usr)] resent the NanoUI resource files to [key_name(victim)] ") else if(href_list["updateicon"]) if(!check_rights(0)) return - var/mob/M = locate(href_list["updateicon"]) - if(!ismob(M)) + var/mob/victim = locate(href_list["updateicon"]) + if(!ismob(victim)) to_chat(usr, "This can only be done to instances of type /mob") return - M.update_icon() + victim.update_icon() else if(href_list["refreshoverlays"]) if(!check_rights(0)) return - var/mob/living/human/H = locate(href_list["refreshoverlays"]) - if(!istype(H)) + var/mob/living/human/victim = locate(href_list["refreshoverlays"]) + if(!istype(victim)) to_chat(usr, "This can only be done to instances of type /mob/living/human") return - H.try_refresh_visible_overlays() + victim.try_refresh_visible_overlays() else if(href_list["adjustDamage"] && href_list["mobToDamage"]) if(!check_rights(R_DEBUG|R_ADMIN|R_FUN)) return - var/mob/living/L = locate(href_list["mobToDamage"]) - if(!istype(L)) return + var/mob/living/victim = locate(href_list["mobToDamage"]) + if(!istype(victim)) return var/Text = href_list["adjustDamage"] var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num - if(!L) + if(!victim) to_chat(usr, "Mob doesn't exist anymore") return if(istext(Text)) - L.take_damage(amount, Text) + victim.take_damage(amount, Text) else - to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") + to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[victim]") return if(amount != 0) - log_admin("[key_name(usr)] dealt [amount] amount of [Text] damage to [L]") - message_admins("[key_name(usr)] dealt [amount] amount of [Text] damage to [L]") + log_admin("[key_name(usr)] dealt [amount] amount of [Text] damage to [victim]") + message_admins("[key_name(usr)] dealt [amount] amount of [Text] damage to [victim]") href_list["datumrefresh"] = href_list["mobToDamage"] else if(href_list["call_proc"]) - var/datum/D = locate(href_list["call_proc"]) - if(istype(D) || istype(D, /client)) // can call on clients too, not just datums - callproc_targetpicked(1, D) + var/datum/callee = locate(href_list["call_proc"]) + if(istype(callee) || istype(callee, /client)) // can call on clients too, not just datums + callproc_targetpicked(1, callee) else if(href_list["addaura"]) if(!check_rights(R_DEBUG|R_ADMIN|R_FUN)) return - var/mob/living/L = locate(href_list["addaura"]) - if(!istype(L)) + var/mob/living/victim = locate(href_list["addaura"]) + if(!istype(victim)) return var/choice = input("Please choose an aura to add", "Auras", null) as null|anything in typesof(/obj/aura) - if(!choice || !L) + if(!choice || !victim) return - var/obj/o = new choice(L) - log_and_message_admins("added \the [o] to \the [L]") + var/obj/new_aura = new choice(victim) + log_and_message_admins("added \the [new_aura] to \the [victim]") else if(href_list["removeaura"]) if(!check_rights(R_DEBUG|R_ADMIN|R_FUN)) return - var/mob/living/L = locate(href_list["removeaura"]) - if(!istype(L)) + var/mob/living/victim = locate(href_list["removeaura"]) + if(!istype(victim)) return - var/choice = input("Please choose an aura to remove", "Auras", null) as null|anything in L.auras - if(!choice || !L) + var/choice = input("Please choose an aura to remove", "Auras", null) as null|anything in victim.auras + if(!choice || !victim) return - log_and_message_admins("removed \the [choice] to \the [L]") + log_and_message_admins("removed \the [choice] to \the [victim]") qdel(choice) else if(href_list["addstressor"]) if(!check_rights(R_DEBUG)) return - var/mob/living/L = locate(href_list["addstressor"]) - if(!istype(L)) + var/mob/living/victim = locate(href_list["addstressor"]) + if(!istype(victim)) return var/static/list/_all_stressors if(!_all_stressors) @@ -637,63 +636,63 @@ if(!stressor) return var/duration = clamp(input("Enter a duration ([STRESSOR_DURATION_INDEFINITE] for permanent).", "Add Stressor") as num|null, STRESSOR_DURATION_INDEFINITE, INFINITY) - if(duration && L.add_stressor(stressor, duration)) - log_and_message_admins("added [stressor] to \the [L] for duration [duration].") + if(duration && victim.add_stressor(stressor, duration)) + log_and_message_admins("added [stressor] to \the [victim] for duration [duration].") else if(href_list["removestressor"]) if(!check_rights(R_DEBUG)) return - var/mob/living/L = locate(href_list["removestressor"]) - if(!istype(L)) + var/mob/living/victim = locate(href_list["removestressor"]) + if(!istype(victim)) return - if(!length(L.stressors)) + if(!length(victim.stressors)) to_chat(usr, "Nothing to remove.") return - var/datum/stressor/stressor = input("Select a stressor to remove.", "Remove Stressor") as null|anything in L.stressors - if(L.remove_stressor(stressor)) - log_and_message_admins("removed [stressor] from \the [L].") + var/datum/stressor/stressor = input("Select a stressor to remove.", "Remove Stressor") as null|anything in victim.stressors + if(victim.remove_stressor(stressor)) + log_and_message_admins("removed [stressor] from \the [victim].") else if(href_list["setstatuscond"]) if(!check_rights(R_DEBUG)) return - var/mob/living/L = locate(href_list["setstatuscond"]) - if(!istype(L)) + var/mob/living/victim = locate(href_list["setstatuscond"]) + if(!istype(victim)) return var/list/all_status_conditions = decls_repository.get_decls_of_subtype(/decl/status_condition) var/list/select_from_conditions = list() for(var/status_cond in all_status_conditions) select_from_conditions += all_status_conditions[status_cond] var/decl/status_condition/selected_condition = input("Select a status condition to set.", "Set Status Condition") as null|anything in select_from_conditions - if(!selected_condition || QDELETED(L) || !check_rights(R_DEBUG)) + if(!selected_condition || QDELETED(victim) || !check_rights(R_DEBUG)) return var/amt = input("Enter an amount to set the condition to.", "Set Status Condition") as num|null - if(isnull(amt) || QDELETED(L) || !check_rights(R_DEBUG)) + if(isnull(amt) || QDELETED(victim) || !check_rights(R_DEBUG)) return if(amt < 0) - amt += GET_STATUS(L, selected_condition.type) - L.set_status(selected_condition.type, amt) - log_and_message_admins("set [selected_condition.name] to [amt] on \the [L].") + amt += GET_STATUS(victim, selected_condition.type) + victim.set_status(selected_condition.type, amt) + log_and_message_admins("set [selected_condition.name] to [amt] on \the [victim].") else if(href_list["setmaterial"]) if(!check_rights(R_DEBUG)) return - var/obj/item/I = locate(href_list["setmaterial"]) - if(!istype(I)) + var/obj/item/item = locate(href_list["setmaterial"]) + if(!istype(item)) to_chat(usr, "This can only be done to instances of type /obj/item") return var/decl/material/new_material = input("Please choose a new material.","Materials",null) as null|anything in decls_repository.get_decls_of_subtype_unassociated(/decl/material) if(!istype(new_material)) return - if(QDELETED(I)) + if(QDELETED(item)) to_chat(usr, "Item doesn't exist anymore") return - I.set_material(new_material.type) - to_chat(usr, "Set material of [I] to [I.get_material()].") + item.set_material(new_material.type) + to_chat(usr, "Set material of [item] to [item.get_material()].") if(href_list["datumrefresh"]) - var/datum/DAT = locate(href_list["datumrefresh"]) - if(istype(DAT, /datum) || istype(DAT, /client)) - debug_variables(DAT) + var/datum/datum_to_refresh = locate(href_list["datumrefresh"]) + if(istype(datum_to_refresh, /datum) || istype(datum_to_refresh, /client)) + debug_variables(datum_to_refresh) return diff --git a/code/modules/atmospherics/he_pipes.dm b/code/modules/atmospherics/he_pipes.dm index 3d13f92bc6d..49437ef28d4 100644 --- a/code/modules/atmospherics/he_pipes.dm +++ b/code/modules/atmospherics/he_pipes.dm @@ -3,6 +3,7 @@ icon = 'icons/atmos/heat.dmi' icon_state = "11" color = "#404040" + pipe_color = "#404040" level = LEVEL_ABOVE_PLATING connect_types = CONNECT_TYPE_HE interact_offline = TRUE //Needs to be set so that pipes don't say they lack power in their description @@ -24,7 +25,6 @@ /obj/machinery/atmospherics/pipe/simple/heat_exchanging/Initialize() . = ..() - color = "#404040" //we don't make use of the fancy overlay system for colours, use this to set the default. add_filter("glow",1, list(type = "drop_shadow", x = 0, y = 0, offset = 0, size = 4)) /obj/machinery/atmospherics/pipe/simple/heat_exchanging/set_dir(new_dir) diff --git a/code/modules/butchery/butchery_data_reptiles.dm b/code/modules/butchery/butchery_data_reptiles.dm index 149a7983db6..f5b1f3fc996 100644 --- a/code/modules/butchery/butchery_data_reptiles.dm +++ b/code/modules/butchery/butchery_data_reptiles.dm @@ -9,6 +9,6 @@ must_use_hook = FALSE gut_type = /obj/item/chems/food/butchery/offal/small -/decl/butchery_data/animal/reptile/drake/harvest_bones(mob/living/donor) +/decl/butchery_data/animal/reptile/space_dragon/harvest_bones(mob/living/donor) . = ..() LAZYADD(., new /obj/item/whip/tail(get_turf(donor))) diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm index e359be5afd3..a06b83ab8f6 100644 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -62,7 +62,7 @@ return 0 if(!holder.cell.check_charge(use_power_cost * CELLRATE)) - to_chat(holder.wearer,"Not enough stored power.") + to_chat(holder.wearer,SPAN_WARNING("Not enough stored power.")) return 0 if(!target) @@ -123,7 +123,7 @@ return 0 if(accepted_item.charges >= 5) - to_chat(user, "Another grenade of that type will not fit into the module.") + to_chat(user, SPAN_DANGER("Another grenade of that type will not fit into the module.")) return 0 to_chat(user, SPAN_BLUE("You slot \the [input_device] into the suit module.")) @@ -139,10 +139,10 @@ if(!target) return 0 - var/mob/living/human/H = holder.wearer + var/mob/living/human/wearer = holder.wearer if(!charge_selected) - to_chat(H, "You have not selected a grenade type.") + to_chat(wearer, SPAN_DANGER("You have not selected a grenade type.")) return 0 var/datum/rig_charge/charge = charges[charge_selected] @@ -151,14 +151,14 @@ return 0 if(charge.charges <= 0) - to_chat(H, "Insufficient grenades!") + to_chat(wearer, SPAN_DANGER("Insufficient grenades!")) return 0 charge.charges-- - var/obj/item/grenade/new_grenade = new charge.product_type(get_turf(H)) - H.visible_message("[H] launches \a [new_grenade]!") + var/obj/item/grenade/new_grenade = new charge.product_type(get_turf(wearer)) + wearer.visible_message(SPAN_DANGER("[wearer] launches \a [new_grenade]!"), SPAN_DANGER("You launch \a [new_grenade]!")) log_and_message_admins("fired a grenade ([new_grenade.name]) from a rigsuit grenade launcher.") - new_grenade.activate(H) + new_grenade.activate(wearer) new_grenade.throw_at(target,fire_force,fire_distance) /obj/item/rig_module/grenade_launcher/cleaner @@ -402,21 +402,21 @@ if(!..()) return FALSE - var/mob/living/H = holder.wearer + var/mob/living/wearer = holder.wearer if(target) var/obj/item/firing = new fabrication_type() firing.dropInto(loc) - H.visible_message(SPAN_DANGER("\The [H] launches \a [firing]!")) + wearer.visible_message(SPAN_DANGER("\The [wearer] launches \a [firing]!"), SPAN_DANGER("You launch \a [firing]!")) firing.throw_at(target,fire_force,fire_distance) else - if(!H.get_empty_hand_slot()) - to_chat(H, SPAN_WARNING("Your hands are full.")) + if(!wearer.get_empty_hand_slot()) + to_chat(wearer, SPAN_WARNING("Your hands are full.")) else var/obj/item/new_weapon = new fabrication_type() - new_weapon.forceMove(H) - to_chat(H, SPAN_BLUE("You quickly fabricate \a [new_weapon].")) - H.put_in_hands(new_weapon) + new_weapon.forceMove(wearer) + to_chat(wearer, SPAN_BLUE("You quickly fabricate \a [new_weapon].")) + wearer.put_in_hands(new_weapon) return TRUE diff --git a/code/modules/economy/cael/Accounts_DB.dm b/code/modules/economy/cael/Accounts_DB.dm index 45ec4157b0f..2fa3a9c13d8 100644 --- a/code/modules/economy/cael/Accounts_DB.dm +++ b/code/modules/economy/cael/Accounts_DB.dm @@ -99,7 +99,7 @@ if("toggle_suspension") if(detailed_account_view) detailed_account_view.suspended = !detailed_account_view.suspended - callHook("change_account_status", list(detailed_account_view)) + RAISE_EVENT(/decl/observ/change_account_status, detailed_account_view) if("finalise_create_account") var/account_name = href_list["holder_name"] @@ -149,7 +149,7 @@ var/funds = detailed_account_view.money detailed_account_view.transfer(station_account, funds, "Revocation of payroll") - callHook("revoke_payroll", list(detailed_account_view)) + RAISE_EVENT(/decl/observ/revoke_payroll, detailed_account_view) if("print") diff --git a/code/modules/emotes/emote_mob.dm b/code/modules/emotes/emote_mob.dm index 616078289de..eec700a5d87 100644 --- a/code/modules/emotes/emote_mob.dm +++ b/code/modules/emotes/emote_mob.dm @@ -152,7 +152,7 @@ return capitalize(pretext) + nametext + subtext /mob/proc/custom_emote(var/m_type = VISIBLE_MESSAGE, var/message = null) - + set waitfor = FALSE if(!can_emote(m_type, src)) return diff --git a/code/modules/events/rogue_drones.dm b/code/modules/events/rogue_drones.dm index aa4733b207e..a6decd690ff 100644 --- a/code/modules/events/rogue_drones.dm +++ b/code/modules/events/rogue_drones.dm @@ -19,7 +19,7 @@ else num = rand(2,6) for(var/i=0, i 0) switch(feels_brute / org.max_damage) if(0 to 0.35) @@ -61,7 +61,7 @@ if(0.65 to INFINITY) status += "throbbing with agony" - var/feels_burn = (org.burn_dam * feels) + var/feels_burn = org.can_feel_pain() ? (org.burn_dam * feels) : 0 if(feels_burn > 0) switch(feels_burn / org.max_damage) if(0 to 0.35) @@ -77,16 +77,16 @@ status += "bleeding" if(org.is_dislocated()) status += "dislocated" - if(org.status & ORGAN_BROKEN) - status += "hurts when touched" + if(org.status & ORGAN_BROKEN && org.can_feel_pain()) + status += "painful to the touch" if(org.status & ORGAN_DEAD) if(BP_IS_PROSTHETIC(org) || BP_IS_CRYSTAL(org)) - status += "is irrecoverably damaged" + status += "irrecoverably damaged" else - status += "is grey and necrotic" + status += "grey and necrotic" else if(org.damage >= org.max_damage && org.germ_level >= INFECTION_LEVEL_TWO) - status += "is likely beyond saving, and has begun to decay" + status += "likely beyond saving and decay has set in" if(!org.is_usable() || org.is_dislocated()) status += "dangling uselessly" @@ -217,6 +217,8 @@ attack.apply_effects(H, src, rand_damage, hit_zone) // Finally, apply damage to target apply_damage(real_damage, attack.get_damage_type(), hit_zone, damage_flags=attack.damage_flags()) + if(istype(ai)) + ai.retaliate(user) return TRUE /mob/living/human/attack_hand(mob/user) diff --git a/code/modules/mob/living/human/human_damage.dm b/code/modules/mob/living/human/human_damage.dm index db5c6936e9c..d4c70923812 100644 --- a/code/modules/mob/living/human/human_damage.dm +++ b/code/modules/mob/living/human/human_damage.dm @@ -101,6 +101,8 @@ else heal_overall_damage(-amount, 0) BITSET(hud_updateflag, HEALTH_HUD) + if(amount > 0 && istype(ai)) + ai.retaliate() /mob/living/human/adjustFireLoss(var/amount, var/do_update_health = TRUE) if(amount > 0) @@ -108,6 +110,8 @@ else heal_overall_damage(0, -amount) BITSET(hud_updateflag, HEALTH_HUD) + if(amount > 0 && istype(ai)) + ai.retaliate() /mob/living/human/getCloneLoss() var/amount = 0 diff --git a/code/modules/mob/living/human/human_defense.dm b/code/modules/mob/living/human/human_defense.dm index 8968e71fdf1..0d127c8afaf 100644 --- a/code/modules/mob/living/human/human_defense.dm +++ b/code/modules/mob/living/human/human_defense.dm @@ -143,7 +143,9 @@ meteor_act visible_message("[src] has been [I.attack_verb.len? pick(I.attack_verb) : "attacked"] in the [affecting.name][weapon_mention] by [user]!") return // If it has no force then no need to do anything else. - return standard_weapon_hit_effects(I, user, effective_force, hit_zone) + . = standard_weapon_hit_effects(I, user, effective_force, hit_zone) + if(istype(ai)) + ai.retaliate(user) /mob/living/human/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) var/obj/item/organ/external/affecting = GET_EXTERNAL_ORGAN(src, hit_zone) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 18588dee914..d6155ed8f03 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -116,8 +116,6 @@ //Body temperature adjusts itself (self-regulation) stabilize_body_temperature() // Only handle AI stuff if we're not being played. - if(!key) - handle_legacy_ai() return TRUE /mob/living/proc/experiences_hunger_and_thirst() @@ -135,10 +133,6 @@ return my_species.thirst_factor return 0 -// Used to handle non-datum AI. -/mob/living/proc/handle_legacy_ai() - return - /mob/living/proc/handle_nutrition_and_hydration() SHOULD_CALL_PARENT(TRUE) if(!experiences_hunger_and_thirst()) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index e61fb103f46..aaacde587de 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -444,7 +444,7 @@ default behaviour is: if(!isturf(loc)) for(var/G in get_active_grabs()) qdel(G) - return + return if(isturf(old_loc)) for(var/atom/movable/AM as anything in ret_grab()) @@ -857,7 +857,7 @@ default behaviour is: /mob/living/proc/set_nutrition(var/amt) nutrition = clamp(amt, 0, get_max_nutrition()) -/mob/living/proc/get_nutrition(var/amt) +/mob/living/proc/get_nutrition() return nutrition /mob/living/proc/adjust_nutrition(var/amt) @@ -1421,7 +1421,7 @@ default behaviour is: return TRUE /mob/living/proc/can_direct_mount(var/mob/user) - if(can_buckle && istype(user) && !user.incapacitated() && user == buckled_mob) + if((user.faction == faction || !faction) && can_buckle && istype(user) && !user.incapacitated() && user == buckled_mob) if(client && a_intent != I_HELP) return FALSE // do not Ratatouille your colleagues // TODO: Piloting skillcheck for hands-free moving? Stupid but amusing @@ -1456,6 +1456,13 @@ default behaviour is: for(var/obj/item/grab/G in buckled_mob.get_held_items()) if(G.get_affecting_mob() == src && !istype(G.current_grab, /decl/grab/simple/control)) qdel(G) + if(istype(ai)) + ai.retaliate(M) + +/mob/living/try_make_grab(mob/living/user, defer_hand = FALSE) + . = ..() + if(istype(ai)) + ai.retaliate(user) /mob/living/can_buckle_mob(var/mob/living/dropping) . = ..() && stat == CONSCIOUS && !buckled && dropping.mob_size <= mob_size @@ -1651,3 +1658,36 @@ default behaviour is: if(istype(I)) return I.get_darksight_range() return get_bodytype()?.eye_darksight_range + +/mob/living/proc/can_act() + return !QDELETED(src) && !incapacitated() + +// Currently only used by AI behaviors +/mob/living/proc/has_ranged_attack() + return FALSE + +/mob/living/proc/get_ranged_attack_distance() + return 0 + +/mob/living/proc/handle_ranged_attack(atom/target) + if(istype(target)) + for(var/obj/item/gun/gun in get_held_items()) + gun.afterattack(target, src, target.Adjacent(src)) + return TRUE + return FALSE + +/mob/living/proc/can_pry_door() + return FALSE + +/mob/living/proc/get_door_pry_time() + return 7 SECONDS + +/mob/living/proc/pry_door(atom/target, pry_time) + return + +/mob/living/proc/turf_is_safe(turf/target) + if(!istype(target)) + return FALSE + if(target.is_open() && target.has_gravity() && !can_overcome_gravity()) + return FALSE + return TRUE diff --git a/code/modules/mob/living/living_damage.dm b/code/modules/mob/living/living_damage.dm index d5048d70dff..54b0776bc84 100644 --- a/code/modules/mob/living/living_damage.dm +++ b/code/modules/mob/living/living_damage.dm @@ -4,6 +4,11 @@ /mob/living/getBruteLoss() return get_max_health() - current_health +/mob/living/adjustBruteLoss(var/amount, var/do_update_health = TRUE) + . = ..() + if(amount > 0 && istype(ai)) + ai.retaliate() + /mob/living/adjustToxLoss(var/amount, var/do_update_health = TRUE) take_damage(amount * 0.5, do_update_health = do_update_health) @@ -12,6 +17,8 @@ /mob/living/adjustFireLoss(var/amount, var/do_update_health = TRUE) take_damage(amount * 0.5, do_update_health = do_update_health) + if(amount > 0 && istype(ai)) + ai.retaliate() /mob/living/setFireLoss(var/amount) take_damage((amount * 0.5)-get_damage(BRUTE)) @@ -21,3 +28,9 @@ /mob/living/setHalLoss(var/amount) take_damage((amount * 0.5)-get_damage(BRUTE)) + +/mob/living/explosion_act() + var/oldhealth = current_health + . = ..() + if(istype(ai) && current_health < oldhealth) + ai.retaliate() diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 7a400561ca7..aae72416bcf 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -20,14 +20,13 @@ . += natural_armor /mob/living/bullet_act(var/obj/item/projectile/P, var/def_zone) - + var/oldhealth = current_health //Being hit while using a deadman switch var/obj/item/assembly/signaler/signaler = get_active_held_item() if(istype(signaler) && signaler.deadman) log_and_message_admins("has triggered a signaler deadman's switch") src.visible_message("[src] triggers their deadman's switch!") signaler.signal() - //Armor var/damage = P.damage var/flags = P.damage_flags() @@ -38,6 +37,8 @@ if(damaged || P.nodamage) // Run the block computation if we did damage or if we only use armor for effects (nodamage) . = get_blocked_ratio(def_zone, P.atom_damage_type, flags, P.armor_penetration, P.damage) P.on_hit(src, ., def_zone) + if(istype(ai) && isliving(P.firer) && !ai.get_target() && current_health < oldhealth && !incapacitated(INCAPACITATION_KNOCKOUT)) + ai.retaliate(P.firer) // For visuals and blood splatters etc /mob/living/proc/bullet_impact_visuals(var/obj/item/projectile/P, var/def_zone, var/damage) @@ -119,6 +120,8 @@ . = standard_weapon_hit_effects(I, user, effective_force, hit_zone) if(I.atom_damage_type == BRUTE && prob(33)) blood_splatter(get_turf(loc), src) + if(istype(ai)) + ai.retaliate(user) //returns 0 if the effects failed to apply for some reason, 1 otherwise. /mob/living/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) @@ -130,6 +133,9 @@ /mob/living/hitby(var/atom/movable/AM, var/datum/thrownthing/TT) . = ..() + if(istype(ai)) + ai.retaliate(TT.thrower) + if(.) if(isliving(AM)) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/_simple_animal.dm similarity index 77% rename from code/modules/mob/living/simple_animal/simple_animal.dm rename to code/modules/mob/living/simple_animal/_simple_animal.dm index 1770d2166ef..36d411b869e 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/_simple_animal.dm @@ -18,27 +18,13 @@ ) var/base_movement_delay = 4 + ai = /datum/mob_controller var/can_have_rider = TRUE var/max_rider_size = MOB_SIZE_SMALL /// Does the percentage health show in the stat panel for the mob? var/show_stat_health = TRUE - /// A prob chance of speaking. - var/speak_chance = 0 - /// Strings shown when this mob speaks and is not understood. - var/list/emote_speech - /// Hearable emotes that this mob can randomly perform. - var/list/emote_hear - /// 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 - - /// 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_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$." @@ -46,7 +32,6 @@ var/response_disarm = "pushes aside" var/response_harm = "kicks" var/harm_intent_damage = 3 - var/can_escape = FALSE // 'smart' simple animals such as human enemies, or things small, big, sharp or strong enough to power out of a net //Temperature effect var/minbodytemp = 250 @@ -102,6 +87,14 @@ // Visible message shown when the mob dies. var/death_message = "dies!" + // Ranged attack handling vars. + var/burst_projectile = FALSE + var/projectiletype + var/projectilesound + var/casingtype + var/fire_desc = "fires" //"X fire_desc at Y!" + var/ranged_range = 6 //tiles of range for ranged attackers to attack + /mob/living/simple_animal/Initialize() . = ..() @@ -125,6 +118,10 @@ /mob/living/simple_animal/proc/setup_languages() add_language(/decl/language/animal) +/mob/living/simple_animal/proc/apply_attack_effects(mob/living/target) + set waitfor = FALSE + return + var/global/list/simplemob_icon_bitflag_cache = list() /mob/living/simple_animal/proc/check_mob_icon_states(var/sa_initializing = FALSE) if(sa_initializing) // Let people force-rebuild the mob cache with proccall if needed. @@ -184,6 +181,7 @@ var/global/list/simplemob_icon_bitflag_cache = list() . = ..() /mob/living/simple_animal/handle_regular_status_updates() + if(purge) purge -= 1 @@ -196,76 +194,16 @@ var/global/list/simplemob_icon_bitflag_cache = list() if(HAS_STATUS(src, STAT_PARA) <= 2) // gated to avoid redundant update_icon() calls. SET_STATUS_MAX(src, STAT_PARA, 3) update_icon() + else + // Cancel any trailing walking if we're dead. + stop_automove() /mob/living/simple_animal/handle_some_updates() . = ..() && (!z || living_observers_present(SSmapping.get_connected_levels(z))) -/mob/living/simple_animal/handle_legacy_ai() - . = ..() - handle_async_life_action() - -// Handles timed stuff in Life() -/mob/living/simple_animal/proc/handle_async_life_action() - set waitfor = FALSE - if(performing_delayed_life_action) - return - if(client) - return - performing_delayed_life_action = TRUE - do_delayed_life_action() - performing_delayed_life_action = FALSE - -/mob/living/simple_animal/proc/turf_is_safe(turf/target) - if(!istype(target)) - return FALSE - if(target.is_open() && target.has_gravity() && !can_overcome_gravity()) - return FALSE - if(is_aquatic != target.submerged()) +/mob/living/simple_animal/turf_is_safe(turf/target) + if((. = ..()) && is_aquatic != target.submerged()) return FALSE - return TRUE - -// For saner overriding; only override this. -/mob/living/simple_animal/proc/do_delayed_life_action() - if(buckled && can_escape) - if(istype(buckled, /obj/effect/energy_net)) - var/obj/effect/energy_net/Net = buckled - Net.escape_net(src) - else if(prob(50)) - escape(src, buckled) - else if(prob(50)) - visible_message("\The [src] struggles against \the [buckled]!") - - //Movement - if(current_posture.prone) - if(!incapacitated()) - set_posture(/decl/posture/standing) - 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)) - var/action = pick( - LAZYLEN(emote_speech); "emote_speech", - LAZYLEN(emote_hear); "emote_hear", - LAZYLEN(emote_see); "emote_see" - ) - switch(action) - if("emote_speech") - if(length(emote_speech)) - say(pick(emote_speech)) - if("emote_hear") - if(length(emote_hear)) - audible_emote("[pick(emote_hear)].") - if("emote_see") - if(length(emote_see)) - visible_emote("[pick(emote_see)].") /mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) . = ..() @@ -308,10 +246,6 @@ var/global/list/simplemob_icon_bitflag_cache = list() 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]!")) - /mob/living/simple_animal/get_gibbed_icon() return icon @@ -380,31 +314,6 @@ var/global/list/simplemob_icon_bitflag_cache = list() return ..() -/mob/living/simple_animal/hit_with_weapon(obj/item/O, mob/living/user, var/effective_force, var/hit_zone) - - visible_message(SPAN_DANGER("\The [src] has been attacked with \the [O] by \the [user]!")) - - if(O.force <= resistance) - to_chat(user, SPAN_WARNING("This weapon is ineffective; it does no damage.")) - return 0 - - var/damage = O.force - if (O.atom_damage_type == PAIN) - damage = 0 - if (O.atom_damage_type == STUN) - damage = (O.force / 8) - if(supernatural && istype(O,/obj/item/nullrod)) - damage *= 2 - purge = 3 - take_damage(damage, O.atom_damage_type, O.damage_flags()) - - return 1 - -/mob/living/simple_animal/take_damage(damage, damage_type = BRUTE, damage_flags, inflicter, armor_pen = 0, silent, do_update_health) - . = ..() - if((damage_type == BRUTE) && (damage_flags & (DAM_EDGE | DAM_SHARP | DAM_BULLET))) // damage flags that should cause bleeding - adjustBleedTicks(damage) - /mob/living/simple_animal/get_movement_delay(var/travel_dir) . = 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. @@ -439,13 +348,6 @@ var/global/list/simplemob_icon_bitflag_cache = list() damage = 30 apply_damage(damage, BRUTE, damage_flags = DAM_EXPLODE) -/mob/living/simple_animal/proc/SA_attackable(target_mob) - if (isliving(target_mob)) - var/mob/living/L = target_mob - if(!L.stat && L.current_health >= 0) - return (0) - return 1 - /mob/living/simple_animal/say(var/message) var/verb = "says" if(speak_emote.len) @@ -501,8 +403,8 @@ var/global/list/simplemob_icon_bitflag_cache = list() /mob/living/simple_animal/get_speech_bubble_state_modifier() return ..() || "rough" -/mob/living/simple_animal/proc/can_act() - return !(QDELETED(src) || incapacitated() || (is_aquatic && !submerged())) +/mob/living/simple_animal/can_act() + return ..() && !(is_aquatic && !submerged()) /mob/living/simple_animal/experiences_hunger_and_thirst() // return !supernatural && !isSynthetic() @@ -585,3 +487,56 @@ var/global/list/simplemob_icon_bitflag_cache = list() /mob/living/simple_animal/handle_stance() stance_damage = 0 return + +/mob/living/simple_animal/proc/get_pry_desc() + return "prying" + +/mob/living/simple_animal/pry_door(var/mob/user, var/delay, var/obj/machinery/door/pesky_door) + if(!can_pry_door()) + return + visible_message(SPAN_DANGER("\The [user] begins [get_pry_desc()] at \the [pesky_door]!")) + if(istype(ai)) + ai.pause() + if(do_after(user, delay, pesky_door)) + pesky_door.open(1) + else + visible_message(SPAN_NOTICE("\The [user] is interrupted.")) + if(istype(ai)) + ai.resume() + +/mob/living/simple_animal/has_ranged_attack() + return !!projectiletype && get_ranged_attack_distance() > 0 + +/mob/living/simple_animal/proc/shoot_wrapper(target, location, user) + if(shoot_at(target, location, user) && casingtype) + new casingtype(loc) + +/mob/living/simple_animal/proc/shoot_at(var/atom/target, var/atom/start) + if(!start) + start = get_turf(src) + if(!can_act() || !istype(target) || !istype(start) || target == start || !has_ranged_attack()) + return FALSE + var/obj/item/projectile/A = new projectiletype(get_turf(start)) + if(!A) + return FALSE + playsound(start, projectilesound, 100, 1) + A.launch(target, get_exposed_defense_zone(target), src) + return TRUE + +/mob/living/simple_animal/get_ranged_attack_distance() + return ranged_range + +/mob/living/simple_animal/handle_ranged_attack(atom/target) + if(!has_ranged_attack() || !istype(target)) + return + visible_message(SPAN_DANGER("\The [src] [fire_desc] at \the [target]!")) + if(burst_projectile) + var/datum/callback/shoot_cb = CALLBACK(src, PROC_REF(shoot_wrapper), target, loc) + addtimer(shoot_cb, 1) + addtimer(shoot_cb, 4) + addtimer(shoot_cb, 6) + else + shoot_at(target, loc, src) + +/mob/living/simple_animal/can_eat_food_currently(obj/eating, mob/user, consumption_method) + return TRUE diff --git a/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm b/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm index b4792456a2c..255d433c5a8 100644 --- a/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm +++ b/code/modules/mob/living/simple_animal/aquatic/_aquatic.dm @@ -1,12 +1,15 @@ /mob/living/simple_animal/aquatic icon = 'icons/mob/simple_animal/fish_content.dmi' - turns_per_wander = 5 mob_size = MOB_SIZE_SMALL - emote_see = list("glubs", "blubs", "bloops") base_animal_type = /mob/living/simple_animal/aquatic is_aquatic = TRUE butchery_data = /decl/butchery_data/animal/fish/small holder_type = /obj/item/holder + ai = /datum/mob_controller/aquatic + +/datum/mob_controller/aquatic + turns_per_wander = 10 + emote_see = list("glubs", "blubs", "bloops") /mob/living/simple_animal/aquatic/Initialize() . = ..() 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 dae75864a02..31c50b14a04 100644 --- a/code/modules/mob/living/simple_animal/aquatic/_aquatic_hostile.dm +++ b/code/modules/mob/living/simple_animal/aquatic/_aquatic_hostile.dm @@ -3,9 +3,13 @@ icon = 'icons/mob/simple_animal/fish_content.dmi' natural_weapon = /obj/item/natural_weapon/bite 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 + ai = /datum/mob_controller/aggressive/aquatic + +/datum/mob_controller/aggressive/aquatic + turns_per_wander = 10 + emote_see = list("gnashes") + 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 21458aa61cf..de4126fee08 100644 --- a/code/modules/mob/living/simple_animal/aquatic/_aquatic_retaliate.dm +++ b/code/modules/mob/living/simple_animal/aquatic/_aquatic_retaliate.dm @@ -1,11 +1,15 @@ -/mob/living/simple_animal/hostile/retaliate/aquatic - abstract_type = /mob/living/simple_animal/hostile/retaliate/aquatic +/mob/living/simple_animal/hostile/aquatic + abstract_type = /mob/living/simple_animal/hostile/aquatic icon = 'icons/mob/simple_animal/fish_content.dmi' natural_weapon = /obj/item/natural_weapon/bite 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 + ai = /datum/mob_controller/aggressive/aquatic + +/datum/mob_controller/aggressive/aquatic + turns_per_wander = 10 + emote_see = list("gnashes") + only_attack_enemies = TRUE diff --git a/code/modules/mob/living/simple_animal/aquatic/aquatic_carp.dm b/code/modules/mob/living/simple_animal/aquatic/aquatic_carp.dm index 944325bb00e..f910bd000f6 100644 --- a/code/modules/mob/living/simple_animal/aquatic/aquatic_carp.dm +++ b/code/modules/mob/living/simple_animal/aquatic/aquatic_carp.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/hostile/retaliate/aquatic/carp +/mob/living/simple_animal/hostile/aquatic/carp name = "carp" desc = "A ferocious fish. May be too hardcore." icon = 'icons/mob/simple_animal/fish_carp.dmi' @@ -6,7 +6,7 @@ max_health = 20 butchery_data = /decl/butchery_data/animal/fish/carp -/mob/living/simple_animal/hostile/retaliate/aquatic/carp/Initialize() +/mob/living/simple_animal/hostile/aquatic/carp/Initialize() . = ..() default_pixel_x = rand(-8,8) default_pixel_y = rand(-8,8) 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 5645b465835..9974995ceb0 100644 --- a/code/modules/mob/living/simple_animal/aquatic/aquatic_sharks.dm +++ b/code/modules/mob/living/simple_animal/aquatic/aquatic_sharks.dm @@ -4,13 +4,16 @@ icon = 'icons/mob/simple_animal/shark.dmi' max_health = 150 natural_weapon = /obj/item/natural_weapon/bite/shark - break_stuff_probability = 15 faction = "sharks" butchery_data = /decl/butchery_data/animal/fish/shark + ai = /datum/mob_controller/aggressive/aquatic/shark /obj/item/natural_weapon/bite/shark force = 20 +/datum/mob_controller/aggressive/aquatic/shark + break_stuff_probability = 15 + /mob/living/simple_animal/hostile/aquatic/shark/huge name = "gigacretoxyrhina" desc = "That is a lot of shark." @@ -19,15 +22,18 @@ /decl/move_intent/walk/animal, /decl/move_intent/run/animal ) - turns_per_wander = 2 - attack_same = 1 mob_size = MOB_SIZE_LARGE pixel_x = -16 max_health = 400 harm_intent_damage = 5 natural_weapon = /obj/item/natural_weapon/bite/giantshark - break_stuff_probability = 35 butchery_data = /decl/butchery_data/animal/fish/shark/large + ai = /datum/mob_controller/aggressive/aquatic/shark/huge + +/datum/mob_controller/aggressive/aquatic/shark/huge + turns_per_wander = 4 + break_stuff_probability = 35 + attack_same_faction = TRUE /obj/item/natural_weapon/bite/giantshark force = 40 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/crow/crow.dm b/code/modules/mob/living/simple_animal/crow/crow.dm index cebe755f81c..101c2d2e3a0 100644 --- a/code/modules/mob/living/simple_animal/crow/crow.dm +++ b/code/modules/mob/living/simple_animal/crow/crow.dm @@ -13,17 +13,15 @@ icon = 'icons/mob/simple_animal/crow.dmi' pass_flags = PASS_FLAG_TABLE mob_size = MOB_SIZE_SMALL - - emote_speech = list("Caw.", "Caw?", "Caw!", "CAW.") speak_emote = list("caws") - emote_hear = list("caws") - emote_see = list("hops") - + ai = /datum/mob_controller/crow natural_weapon = /obj/item/natural_weapon/crow_claws - - stop_wandering = TRUE universal_speak = TRUE +/datum/mob_controller/crow + emote_speech = list("Caw.", "Caw?", "Caw!", "CAW.") + emote_hear = list("caws") + emote_see = list("hops") /mob/living/simple_animal/crow/get_overlay_state_modifier() return (stat == DEAD) ? "-dead" : null diff --git a/code/modules/mob/living/simple_animal/familiars/familiars.dm b/code/modules/mob/living/simple_animal/familiars/familiars.dm index 6d35cc62bfb..309a8521d7e 100644 --- a/code/modules/mob/living/simple_animal/familiars/familiars.dm +++ b/code/modules/mob/living/simple_animal/familiars/familiars.dm @@ -27,7 +27,10 @@ max_health = 200 natural_weapon = /obj/item/natural_weapon/pincers/strong resistance = 9 - can_escape = TRUE //snip snip + ai = /datum/mob_controller/familiar_crab + +/datum/mob_controller/familiar_crab + can_escape_buckles = TRUE /obj/item/natural_weapon/pincers/strong force = 15 @@ -45,9 +48,12 @@ speak_emote = list("gnashes") max_health = 100 natural_weapon = /obj/item/natural_weapon/bite - can_escape = TRUE min_gas = null wizardy_spells = list(/spell/aoe_turf/conjure/forcewall) + ai = /datum/mob_controller/familiar_pike + +/datum/mob_controller/familiar_pike + can_escape_buckles = TRUE /mob/living/simple_animal/familiar/pike/Process_Spacemove() return 1 //No drifting in space for space carp! //original comments do not steal @@ -103,9 +109,11 @@ response_harm = "stamps on" max_health = 15 natural_weapon = /obj/item/natural_weapon/bite/mouse - can_escape = TRUE - wizardy_spells = list(/spell/aoe_turf/smoke) + ai = /datum/mob_controller/familiar_mouse + +/datum/mob_controller/familiar_mouse + can_escape_buckles = TRUE /mob/living/simple_animal/familiar/pet/mouse/Initialize() . = ..() diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index f0623130d0e..944147201f6 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -1,14 +1,51 @@ +/datum/mob_controller/passive/hunter/cat + emote_speech = list("Meow!","Esp!","Purr!","HSSSSS") + emote_hear = list("meows","mews") + emote_see = list("shakes their head", "shivers") + speak_chance = 0.25 + turns_per_wander = 10 + +/datum/mob_controller/passive/hunter/cat/try_attack_prey(mob/living/prey) + var/mob/living/simple_animal/passive/mouse/mouse = prey + if(istype(mouse)) + mouse.splat() + return + return ..() + +/datum/mob_controller/passive/hunter/cat/consume_prey(mob/living/prey) + next_hunt = world.time + rand(1 SECONDS, 10 SECONDS) + +/datum/mob_controller/passive/hunter/cat/can_hunt(mob/living/victim) + return istype(victim, /mob/living/simple_animal/passive/mouse) && !victim.stat + +/datum/mob_controller/passive/hunter/cat/update_targets() + . = ..() + if(!flee_target) + for(var/mob/living/simple_animal/passive/mouse/snack in oview(body, 5)) + if(snack.stat != DEAD && prob(15)) + body.custom_emote(AUDIBLE_MESSAGE, pick("hisses and spits!", "mrowls fiercely!", "eyes [snack] hungrily.")) + break + +/datum/mob_controller/passive/hunter/cat/do_process() + . = ..() + if(!hunt_target && !flee_target && prob(1)) //spooky + var/mob/observer/ghost/spook = locate() in range(body, 5) + if(spook) + var/turf/T = spook.loc + var/list/visible = list() + for(var/obj/O in T.contents) + if(!O.invisibility && O.name) + visible += O + if(visible.len) + var/atom/A = pick(visible) + body.custom_emote(VISIBLE_MESSAGE, "suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") + //Cat -/mob/living/simple_animal/cat +/mob/living/simple_animal/passive/cat name = "cat" desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." icon = 'icons/mob/simple_animal/cat_calico.dmi' - emote_speech = list("Meow!","Esp!","Purr!","HSSSSS") speak_emote = list("purrs", "meows") - emote_hear = list("meows","mews") - emote_see = list("shakes their head", "shivers") - speak_chance = 0.5 - turns_per_wander = 5 see_in_dark = 6 minbodytemp = 223 //Below -50 Degrees Celsius maxbodytemp = 323 //Above 50 Degrees Celsius @@ -17,12 +54,13 @@ possession_candidate = 1 pass_flags = PASS_FLAG_TABLE butchery_data = /decl/butchery_data/animal/cat - base_animal_type = /mob/living/simple_animal/cat + base_animal_type = /mob/living/simple_animal/passive/cat + ai = /datum/mob_controller/passive/hunter/cat var/turns_since_scan = 0 var/mob/living/simple_animal/passive/mouse/movement_target var/mob/flee_target -/mob/living/simple_animal/cat/get_bodytype() +/mob/living/simple_animal/passive/cat/get_bodytype() return GET_DECL(/decl/bodytype/quadruped/animal/cat) /decl/bodytype/quadruped/animal/cat/Initialize() @@ -36,181 +74,96 @@ ) . = ..() -/mob/living/simple_animal/cat/do_delayed_life_action() - ..() - //MICE! - if((src.loc) && isturf(src.loc)) - if(!current_posture.prone && !buckled) - for(var/mob/living/simple_animal/passive/mouse/M in loc) - if(!M.stat) - M.splat() - visible_emote(pick("bites \the [M]!","toys with \the [M].","chomps on \the [M]!")) - movement_target = null - stop_wandering = FALSE - break - - for(var/mob/living/simple_animal/passive/mouse/snack in oview(src,5)) - if(snack.stat < DEAD && prob(15)) - audible_emote(pick("hisses and spits!","mrowls fiercely!","eyes [snack] hungrily.")) - break - - turns_since_scan++ - if (turns_since_scan > 5) - stop_automove() - turns_since_scan = 0 - - if (flee_target) //fleeing takes precendence - handle_flee_target() - else - handle_movement_target() - - if(prob(2)) //spooky - var/mob/observer/ghost/spook = locate() in range(src,5) - if(spook) - var/turf/T = spook.loc - var/list/visible = list() - for(var/obj/O in T.contents) - if(!O.invisibility && O.name) - visible += O - if(visible.len) - var/atom/A = pick(visible) - visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") - -/mob/living/simple_animal/cat/proc/handle_movement_target() - //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_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_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) - 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_wandering = FALSE - - if (flee_target) - if(prob(25)) say("HSSSSS") - 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) - flee_target = A - turns_since_scan = 5 - -/mob/living/simple_animal/cat/attackby(var/obj/item/O, var/mob/user) - . = ..() - if(O.force) - set_flee_target(user? user : src.loc) +//Basic friend AI +/mob/living/simple_animal/passive/cat/fluff + ai = /datum/mob_controller/passive/hunter/cat/friendly -/mob/living/simple_animal/cat/default_hurt_interaction(mob/user) - . = ..() - if(.) - set_flee_target(user) +/datum/mob_controller/passive/hunter/cat/friendly + var/befriend_job = null + var/atom/movement_target -/mob/living/simple_animal/cat/explosion_act() - . = ..() - set_flee_target(src.loc) +/datum/mob_controller/passive/hunter/cat/friendly/add_friend(mob/friend) + if(length(get_friends()) > 1 || !ishuman(friend)) + return FALSE + var/mob/living/human/human_friend = friend + if(befriend_job && human_friend.job != befriend_job) + return FALSE + return ..() -/mob/living/simple_animal/cat/bullet_act(var/obj/item/projectile/proj) - . = ..() - set_flee_target(isliving(proj.firer) ? proj.firer : src.loc) +/datum/mob_controller/passive/hunter/cat/friendly/do_process() -/mob/living/simple_animal/cat/hitby(atom/movable/AM, var/datum/thrownthing/TT) . = ..() - set_flee_target(TT.thrower? TT.thrower : src.loc) -//Basic friend AI -/mob/living/simple_animal/cat/fluff + // Get our friend. + var/list/friends = get_friends() var/mob/living/human/friend - var/befriend_job = null + if(LAZYLEN(friends)) + var/weakref/friend_ref = friends[1] + friend = friend_ref.resolve() + + if(body.stat || hunt_target || flee_target || QDELETED(friend)) + return -/mob/living/simple_animal/cat/fluff 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/current_dist = get_dist(body, friend) + + if (movement_target != friend) + if (current_dist > follow_dist && (friend in oview(body))) + //stop existing movement + body.stop_automove() + turns_since_scan = 0 + + //walk to friend + stop_wandering() + movement_target = friend + body.start_automove(movement_target, metadata = new /datum/automove_metadata(_acceptable_distance = near_dist)) + + //already following and close enough, stop + else if (current_dist <= near_dist) + body.stop_automove() + movement_target = null + resume_wandering() + if (prob(10)) + body.say("Meow!") -/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/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 - stop_automove() - turns_since_scan = 0 - - //walk to friend - stop_wandering = TRUE - movement_target = friend - start_automove(movement_target) - - //already following and close enough, stop - else if (current_dist <= near_dist) - stop_automove() - movement_target = null - stop_wandering = FALSE - if (prob(10)) - say("Meow!") - - if (QDELETED(friend) || movement_target != friend) - ..() - -/mob/living/simple_animal/cat/fluff/do_delayed_life_action() - ..() - if (stat || QDELETED(friend)) - return - if (get_dist(src, friend) <= 1) + if (get_dist(body, friend) <= 1) if (friend.stat >= DEAD || friend.is_asystole()) - if (prob((friend.stat < DEAD)? 50 : 15)) + if (prob((friend.stat < DEAD)? 25 : 7.5)) var/verb = pick("meows", "mews", "mrowls") - audible_emote(pick("[verb] in distress.", "[verb] anxiously.")) - else - if (prob(5)) - visible_emote(pick("nuzzles [friend].", - "brushes against [friend].", - "rubs against [friend].", - "purrs.")) - else if (friend.current_health <= 50) - if (prob(10)) - var/verb = pick("meows", "mews", "mrowls") - audible_emote("[verb] anxiously.") - -/mob/living/simple_animal/cat/fluff/verb/become_friends() + body.custom_emote(AUDIBLE_MESSAGE, pick("[verb] in distress.", "[verb] anxiously.")) + else if (prob(5)) + body.custom_emote( + VISIBLE_MESSAGE, + pick("nuzzles [friend].","brushes against [friend].","rubs against [friend].","purrs.") + ) + else if (friend.current_health <= 50 && prob(5)) + var/verb = pick("meows", "mews", "mrowls") + body.custom_emote(AUDIBLE_MESSAGE, "[verb] anxiously.") + +/mob/living/simple_animal/passive/cat/fluff/verb/become_friends() set name = "Become Friends" set category = "IC" set src in view(1) + if(!istype(ai)) + return + + var/list/friends = ai?.get_friends() + if(!LAZYLEN(friends)) + return + + var/weakref/current_friend = friends[1] + var/mob/friend = current_friend?.resolve() + if(!friend) var/mob/living/human/H = usr - if(istype(H) && (!befriend_job || H.job == befriend_job)) - friend = usr - . = 1 + if(istype(H)) + . = ai?.add_friend(usr) else if(usr == friend) . = 1 //already friends, but show success anyways @@ -225,7 +178,7 @@ return //RUNTIME IS ALIVE! SQUEEEEEEEE~ -/mob/living/simple_animal/cat/fluff/runtime +/mob/living/simple_animal/passive/cat/fluff/runtime name = "Runtime" desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." gender = FEMALE @@ -236,14 +189,14 @@ /obj/item/holder/runtime origin_tech = @'{"programming":1,"biotech":1}' -/mob/living/simple_animal/cat/kitten +/mob/living/simple_animal/passive/cat/kitten name = "kitten" desc = "D'aaawwww" icon = 'icons/mob/simple_animal/kitten.dmi' gender = NEUTER butchery_data = /decl/butchery_data/animal/cat/kitten -/mob/living/simple_animal/cat/kitten/get_bodytype() +/mob/living/simple_animal/passive/cat/kitten/get_bodytype() return GET_DECL(/decl/bodytype/quadruped/animal/kitten) /decl/bodytype/quadruped/animal/kitten/Initialize() @@ -257,11 +210,11 @@ ) . = ..() -/mob/living/simple_animal/cat/kitten/Initialize() +/mob/living/simple_animal/passive/cat/kitten/Initialize() . = ..() gender = pick(MALE, FEMALE) -/mob/living/simple_animal/cat/fluff/ran +/mob/living/simple_animal/passive/cat/fluff/ran name = "Runtime" desc = "Under no circumstances is this feline allowed inside the atmospherics system." gender = FEMALE diff --git a/code/modules/mob/living/simple_animal/friendly/corgi.dm b/code/modules/mob/living/simple_animal/friendly/corgi.dm index f216fb447de..cc68ced5e29 100644 --- a/code/modules/mob/living/simple_animal/friendly/corgi.dm +++ b/code/modules/mob/living/simple_animal/friendly/corgi.dm @@ -5,11 +5,6 @@ desc = "It's a corgi." icon = 'icons/mob/simple_animal/corgi.dmi' speak_emote = list("barks", "woofs") - emote_speech = list("YAP", "Woof!", "Bark!", "AUUUUUU") - emote_hear = list("barks", "woofs", "yaps","pants") - emote_see = list("shakes its head", "shivers") - speak_chance = 0.5 - turns_per_wander = 10 response_disarm = "bops" see_in_dark = 5 mob_size = MOB_SIZE_SMALL @@ -19,6 +14,26 @@ base_animal_type = /mob/living/simple_animal/corgi can_buckle = TRUE butchery_data = /decl/butchery_data/animal/corgi + ai = /datum/mob_controller/corgi + +/datum/mob_controller/corgi + emote_speech = list("YAP", "Woof!", "Bark!", "AUUUUUU") + emote_hear = list("barks", "woofs", "yaps","pants") + emote_see = list("shakes its head", "shivers") + speak_chance = 0.25 + turns_per_wander = 20 + +/datum/mob_controller/corgi/proc/dance() + set waitfor = FALSE + if(QDELETED(body) || body.client) + return + var/decl/pronouns/pronouns = body.get_pronouns() + body.custom_emote(VISIBLE_MESSAGE, pick("dances around.","chases [pronouns.his] tail.")) + for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2)) + body.set_dir(i) + sleep(1) + if(QDELETED(body) || body.client) + return /mob/living/simple_animal/corgi/get_bodytype() return GET_DECL(/decl/bodytype/quadruped/animal/corgi) @@ -40,58 +55,73 @@ real_name = "Ian" //Intended to hold the name without altering it. gender = MALE desc = "It's a corgi." + ai = /datum/mob_controller/corgi/ian + +/datum/mob_controller/corgi/ian var/turns_since_scan = 0 var/obj/movement_target -/mob/living/simple_animal/corgi/Ian/do_delayed_life_action() - ..() +/datum/mob_controller/corgi/ian/proc/go_get_lunch() + set waitfor = FALSE + + if(!movement_target || !body || body.stat) + return + + stop_wandering() + step_to(body, movement_target,1) + sleep(3) + if(!movement_target || !body || body.stat) + return + + step_to(body, movement_target,1) + sleep(3) + if(!movement_target || !body || body.stat) + return + + step_to(body, movement_target,1) + if(!movement_target || !body || body.stat) + return + + if (movement_target.loc.x < body.x) + body.set_dir(WEST) + else if (movement_target.loc.x > body.x) + body.set_dir(EAST) + else if (movement_target.loc.y < body.y) + body.set_dir(SOUTH) + else if (movement_target.loc.y > body.y) + body.set_dir(NORTH) + else + body.set_dir(SOUTH) + if(isturf(movement_target.loc) && body.Adjacent(movement_target)) + body.UnarmedAttack(movement_target) + else if(ishuman(movement_target.loc) && prob(20)) + body.custom_emote(VISIBLE_MESSAGE, "stares at the [movement_target] that [movement_target.loc] has with sad puppy eyes.") + +/datum/mob_controller/corgi/ian/do_process(time_elapsed) + . = ..() + if(body.stat || body.current_posture?.prone || body.buckled) + return + //Feeding, chasing food, FOOOOODDDD - if(!current_posture.prone && !buckled) - turns_since_scan++ - if(turns_since_scan > 5) - turns_since_scan = 0 - if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) - movement_target = null - stop_wandering = FALSE - if( !movement_target || !(movement_target.loc in oview(src, 3)) ) - movement_target = null - 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_wandering = TRUE - step_to(src,movement_target,1) - sleep(3) - step_to(src,movement_target,1) - sleep(3) - step_to(src,movement_target,1) - - if(movement_target) //Not redundant due to sleeps, Item can be gone in 6 decisecomds - if (movement_target.loc.x < src.x) - set_dir(WEST) - else if (movement_target.loc.x > src.x) - set_dir(EAST) - else if (movement_target.loc.y < src.y) - set_dir(SOUTH) - else if (movement_target.loc.y > src.y) - set_dir(NORTH) - else - set_dir(SOUTH) - - if(isturf(movement_target.loc) && Adjacent(movement_target)) - UnarmedAttack(movement_target) - else if(ishuman(movement_target.loc) && prob(20)) - visible_emote("stares at the [movement_target] that [movement_target.loc] has with sad puppy eyes.") - - if(prob(1)) - visible_emote(pick("dances around.","chases their tail.")) - for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2)) - set_dir(i) - sleep(1) - if(QDELETED(src) || client) - return + turns_since_scan++ + if(turns_since_scan > 10) + turns_since_scan = 0 + if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) + movement_target = null + resume_wandering() + if( !movement_target || !(movement_target.loc in oview(body, 3)) ) + movement_target = null + resume_wandering() + for(var/obj/item/chems/food/S in oview(body, 3)) + if(isturf(S.loc) || ishuman(S.loc)) + movement_target = S + break + if(movement_target) + go_get_lunch() + return + + if(prob(1)) + dance() /mob/living/simple_animal/corgi/attackby(var/obj/item/O, var/mob/user) //Marker -Agouri if(istype(O, /obj/item/newspaper)) @@ -144,8 +174,40 @@ gender = FEMALE desc = "It's a corgi with a cute pink bow." icon = 'icons/mob/simple_animal/corgi_lisa.dmi' - var/turns_since_scan = 0 + ai = /datum/mob_controller/corgi/lisa + +/datum/mob_controller/corgi/lisa var/puppies = 0 + var/turns_since_scan = 0 + +/datum/mob_controller/corgi/lisa/do_process(time_elapsed) + . = ..() + if(body.stat || body.current_posture?.prone || body.buckled) + return + + turns_since_scan++ + if(turns_since_scan > 30) + turns_since_scan = 0 + var/alone = 1 + var/mob/living/ian + for(var/mob/M in oviewers(7, body)) + if(istype(M, /mob/living/simple_animal/corgi/Ian)) + if(M.client) + alone = 0 + break + else + ian = M + else + alone = 0 + break + if(alone && ian && puppies < 4) + if(body.near_camera() || ian.near_camera()) + return + new /mob/living/simple_animal/corgi/puppy(body.loc) + puppies++ + + if(prob(1)) + dance() //Lisa already has a cute bow! /mob/living/simple_animal/corgi/Lisa/OnTopic(mob/user, href_list) @@ -153,34 +215,3 @@ to_chat(user, "[src] already has a cute bow!") return TOPIC_HANDLED return ..() - -/mob/living/simple_animal/corgi/Lisa/do_delayed_life_action() - ..() - if(!current_posture.prone && !buckled) - turns_since_scan++ - if(turns_since_scan > 15) - turns_since_scan = 0 - var/alone = 1 - var/ian = 0 - for(var/mob/M in oviewers(7, src)) - if(istype(M, /mob/living/simple_animal/corgi/Ian)) - if(M.client) - alone = 0 - break - else - ian = M - else - alone = 0 - break - if(alone && ian && puppies < 4) - if(near_camera(src) || near_camera(ian)) - return - new /mob/living/simple_animal/corgi/puppy(loc) - - if(prob(1)) - visible_emote(pick("dances around","chases her tail")) - for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2)) - set_dir(i) - sleep(1) - if(QDELETED(src) || client) - return diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm index 5b7c22f062c..244367bfe20 100644 --- a/code/modules/mob/living/simple_animal/friendly/crab.dm +++ b/code/modules/mob/living/simple_animal/friendly/crab.dm @@ -5,14 +5,8 @@ icon = 'icons/mob/simple_animal/crab.dmi' mob_size = MOB_SIZE_SMALL speak_emote = list("clicks") - emote_hear = list("clicks") - emote_see = list("clacks") - speak_chance = 0.5 - turns_per_wander = 5 response_harm = "stamps on" - stop_wandering = TRUE possession_candidate = 1 - can_escape = TRUE //snip snip pass_flags = PASS_FLAG_TABLE natural_armor = list( ARMOR_MELEE = ARMOR_MELEE_KNIVES @@ -20,6 +14,14 @@ ai = /datum/mob_controller/crab butchery_data = /decl/butchery_data/animal/arthropod/crab +/datum/mob_controller/crab + emote_hear = list("clicks") + emote_see = list("clacks") + speak_chance = 0.25 + turns_per_wander = 10 + wander_directions = list(EAST, WEST) // they only go sideways... + can_escape_buckles = TRUE //snip snip + // TODO /decl/bodytype/hexapod/animal abstract_type = /decl/bodytype/hexapod/animal @@ -44,19 +46,6 @@ ) . = ..() -/datum/mob_controller/crab - expected_type = /mob/living/simple_animal/crab - -/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_wander++ - if(crab.turns_since_wander >= crab.turns_per_wander) - crab.Move(get_step(crab,pick(4,8))) - crab.turns_since_wander = 0 - //COFFEE! SQUEEEEEEEEE! /mob/living/simple_animal/crab/Coffee name = "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 3b535efe090..af5e498381c 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -1,75 +1,111 @@ //goat -/mob/living/simple_animal/hostile/retaliate/goat +/mob/living/simple_animal/hostile/goat name = "goat" desc = "Not known for their pleasant disposition." icon = 'icons/mob/simple_animal/goat.dmi' speak_emote = list("brays") - emote_speech = list("EHEHEHEHEH","eh?") - emote_hear = list("brays") - emote_see = list("shakes its head", "stamps a foot", "glares around") - speak_chance = 0.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/mob_controller/goat + ai = /datum/mob_controller/aggressive/goat var/datum/reagents/udder = null -/datum/mob_controller/goat - expected_type = /mob/living/simple_animal/hostile/retaliate/goat +/datum/mob_controller/aggressive/goat + expected_type = /mob/living/simple_animal/hostile/goat + emote_speech = list("EHEHEHEHEH","eh?") + emote_hear = list("brays") + emote_see = list("shakes its head", "stamps a foot", "glares around") + speak_chance = 0.25 + turns_per_wander = 10 + only_attack_enemies = TRUE + +/datum/mob_controller/aggressive/goat/retaliate(atom/source) + ..() + if(body?.stat == CONSCIOUS && prob(50)) + var/decl/pronouns/pronouns = body.get_pronouns() + body.visible_message(SPAN_DANGER("\The [body] gets an evil-looking gleam in [pronouns.his] eye.")) + +/datum/mob_controller/aggressive/goat/proc/find_edible_atom(list/targets) + // TODO: add /obj/structure/flora here and in goat/UnarmedAttack() + var/atom/maybe_food = locate(/obj/effect/vine) in targets + if(!istype(maybe_food)) + for(var/obj/machinery/portable_atmospherics/hydroponics/tray in targets) + if(tray.seed) + maybe_food = tray + break + if(istype(maybe_food)) + if(get_dist(body, maybe_food) > 1 || body.Adjacent(maybe_food)) + return maybe_food + +/datum/mob_controller/aggressive/goat/do_process(time_elapsed) + + . = ..() + if(QDELETED(body) || body.stat) + return -/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)) - goat.Retaliate() - - if(length(goat.enemies) && prob(10)) - goat.enemies = list() - goat.LoseTarget() - goat.visible_message(SPAN_NOTICE("\The [goat] calms down.")) - - var/obj/effect/vine/SV = locate() in goat.loc - if(SV) - if(prob(60)) - goat.visible_message(SPAN_NOTICE("\The [goat] eats the plants.")) - SV.die_off(1) - var/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/SP = locate() in goat.loc - if(SP) - qdel(SP) - else if(prob(20)) - goat.visible_message(SPAN_NOTICE("\The [goat] chews on the plants.")) + var/list/enemies = get_enemies() + if(!LAZYLEN(enemies) && prob(0.5)) + retaliate() + + if(LAZYLEN(enemies) && prob(5)) + clear_enemies() + lose_target() + body.visible_message(SPAN_NOTICE("\The [body] calms down.")) + + if(get_target()) return - if(!LAZYLEN(goat.grabbed_by)) - var/obj/effect/vine/food = locate(/obj/effect/vine) in oview(5,goat.loc) - if(food) - var/step = get_step_to(goat, food, 0) - goat.Move(step) + var/atom/food = find_edible_atom(view(1, body.loc)) + if(istype(food)) + body.stop_automove() + body.a_intent = I_HELP + body.ClickOn(food) + else if(!LAZYLEN(body.grabbed_by)) + food = find_edible_atom(oview(5, body.loc)) + if(istype(food)) + body.start_automove(food) + else + body.stop_automove() + else + body.stop_automove() -/mob/living/simple_animal/hostile/retaliate/goat/Initialize() +/mob/living/simple_animal/hostile/goat/Initialize() . = ..() udder = new(50, src) -/mob/living/simple_animal/hostile/retaliate/goat/Destroy() +/mob/living/simple_animal/hostile/goat/UnarmedAttack(var/atom/A, var/proximity) + var/was_food = FALSE + if(proximity) + if(prob(30)) + if(istype(A, /obj/effect/vine)) + var/obj/effect/vine/SV = A + SV.die_off(1) + was_food = TRUE + else if(istype(A, /obj/machinery/portable_atmospherics/hydroponics)) + var/obj/machinery/portable_atmospherics/hydroponics/tray = A + if(tray.seed) + was_food = TRUE + tray.die() + if(!QDELETED(tray)) + tray.remove_dead(silent = TRUE) // this will qdel invisible trays + if(was_food) + visible_message(SPAN_NOTICE("\The [src] eats \the [A].")) + + return was_food ? TRUE :..() + +/mob/living/simple_animal/hostile/goat/Destroy() QDEL_NULL(udder) . = ..() -/mob/living/simple_animal/hostile/retaliate/goat/handle_living_non_stasis_processes() - . = ..() - if(!.) - return FALSE - if(stat == CONSCIOUS && udder && prob(5)) +/mob/living/simple_animal/hostile/goat/handle_living_non_stasis_processes() + if((. = ..()) && stat == CONSCIOUS && udder && prob(5)) udder.add_reagent(/decl/material/liquid/drink/milk, rand(5, 10)) -/mob/living/simple_animal/hostile/retaliate/goat/Retaliate() - ..() - if(stat == CONSCIOUS && prob(50)) - visible_message("\The [src] gets an evil-looking gleam in their eye.") - -/mob/living/simple_animal/hostile/retaliate/goat/attackby(var/obj/item/O, var/mob/user) +/mob/living/simple_animal/hostile/goat/attackby(var/obj/item/O, var/mob/user) var/obj/item/chems/glass/G = O if(stat == CONSCIOUS && istype(G) && ATOM_IS_OPEN_CONTAINER(G)) user.visible_message("[user] milks [src] using \the [O].") @@ -87,11 +123,7 @@ desc = "Known for their milk, just don't tip them over." icon = 'icons/mob/simple_animal/cow.dmi' speak_emote = list("moos","moos hauntingly") - emote_speech = list("moo?","moo","MOOOOOO") - emote_hear = list("brays") - emote_see = list("shakes its head") - speak_chance = 0.5 - turns_per_wander = 5 + ai = /datum/mob_controller/cow see_in_dark = 6 max_health = 50 butchery_data = /decl/butchery_data/animal/ruminant/cow @@ -104,6 +136,13 @@ "seems resigned to its fate" ) +/datum/mob_controller/cow + emote_speech = list("moo?","moo","MOOOOOO") + emote_hear = list("brays") + emote_see = list("shakes its head") + speak_chance = 0.25 + turns_per_wander = 10 + /mob/living/simple_animal/cow/Initialize() . = ..() udder = new(50, src) @@ -129,10 +168,7 @@ . = ..() /mob/living/simple_animal/cow/handle_living_non_stasis_processes() - . = ..() - if(!.) - return FALSE - if(udder && prob(5)) + if((. = ..()) && udder && prob(5)) udder.add_reagent(/decl/material/liquid/drink/milk, rand(5, 10)) /mob/living/simple_animal/cow/default_disarm_interaction(mob/user) @@ -152,41 +188,45 @@ desc = "Adorable! They make such a racket though." icon = 'icons/mob/simple_animal/chick.dmi' speak_emote = list("cheeps") - emote_speech = list("Cherp.","Cherp?","Chirrup.","Cheep!") - emote_hear = list("cheeps") - emote_see = list("pecks at the ground","flaps its tiny wings") - speak_chance = 1 - turns_per_wander = 2 max_health = 1 pass_flags = PASS_FLAG_TABLE | PASS_FLAG_GRILLE mob_size = MOB_SIZE_MINISCULE butchery_data = /decl/butchery_data/animal/small/fowl/chicken/chick + ai = /datum/mob_controller/chick var/amount_grown = 0 +/datum/mob_controller/chick + emote_speech = list("Cherp.","Cherp?","Chirrup.","Cheep!") + emote_hear = list("cheeps") + emote_see = list("pecks at the ground","flaps its tiny wings") + speak_chance = 0.5 + turns_per_wander = 4 + /mob/living/simple_animal/chick/Initialize() . = ..() pixel_x = rand(-6, 6) pixel_y = rand(0, 10) /mob/living/simple_animal/chick/handle_living_non_stasis_processes() - . = ..() - if(!.) - return FALSE - amount_grown += rand(1,2) - if(amount_grown >= 100) - new /mob/living/simple_animal/fowl/chicken(src.loc) - qdel(src) + if((. = ..())) + amount_grown += rand(1,2) + if(amount_grown >= 100) + new /mob/living/simple_animal/fowl/chicken(src.loc) + qdel(src) /mob/living/simple_animal/fowl max_health = 10 pass_flags = PASS_FLAG_TABLE mob_size = MOB_SIZE_SMALL butchery_data = /decl/butchery_data/animal/small/fowl - speak_chance = 2 - turns_per_wander = 3 + ai = /datum/mob_controller/fowl abstract_type = /mob/living/simple_animal/fowl var/body_color +/datum/mob_controller/fowl + speak_chance = 1 + turns_per_wander = 6 + /mob/living/simple_animal/fowl/Initialize() if(!default_pixel_x) default_pixel_x = rand(-6, 6) @@ -201,11 +241,14 @@ var/global/chicken_count = 0 desc = "Hopefully the eggs are good this season." icon = 'icons/mob/simple_animal/chicken_white.dmi' speak_emote = list("clucks","croons") + butchery_data = /decl/butchery_data/animal/small/fowl/chicken + ai = /datum/mob_controller/fowl/chicken + var/eggsleft = 0 + +/datum/mob_controller/fowl/chicken emote_speech = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") emote_hear = list("clucks") emote_see = list("pecks at the ground","flaps its wings viciously") - butchery_data = /decl/butchery_data/animal/small/fowl/chicken - var/eggsleft = 0 /mob/living/simple_animal/fowl/chicken/Initialize() . = ..() @@ -245,10 +288,7 @@ var/global/chicken_count = 0 ..() /mob/living/simple_animal/fowl/chicken/handle_living_non_stasis_processes() - . = ..() - if(!.) - return FALSE - if(prob(3) && eggsleft > 0) + if((. = ..()) && prob(3) && eggsleft > 0) visible_message("[src] [pick("lays an egg.","squats down and croons.","begins making a huge racket.","begins clucking raucously.")]") eggsleft-- var/obj/item/chems/food/egg/E = new(get_turf(src)) @@ -263,10 +303,13 @@ var/global/chicken_count = 0 desc = "It's a duck. Quack." icon = 'icons/mob/simple_animal/duck_white.dmi' speak_emote = list("quacks") + butchery_data = /decl/butchery_data/animal/small/fowl/duck + ai = /datum/mob_controller/fowl/duck + +/datum/mob_controller/fowl/duck emote_speech = list("Wak!","Wak wak wak!","Wak wak.") emote_hear = list("quacks") emote_see = list("preens itself", "waggles its tail") - butchery_data = /decl/butchery_data/animal/small/fowl/duck /mob/living/simple_animal/fowl/duck/Initialize() . = ..() diff --git a/code/modules/mob/living/simple_animal/friendly/frog.dm b/code/modules/mob/living/simple_animal/friendly/frog.dm index cabb786a320..f268abfe0c0 100644 --- a/code/modules/mob/living/simple_animal/friendly/frog.dm +++ b/code/modules/mob/living/simple_animal/friendly/frog.dm @@ -3,16 +3,19 @@ desc = "A small, slimy amphibian. Likes to eat flies." icon = 'icons/mob/simple_animal/frog_green.dmi' speak_emote = list("ribbits","croaks") - emote_speech = list("Ribbit!","Riiibit!") - emote_hear = list("ribbits","croaks") - emote_see = list("hops","inflates its vocal sac","catches a fly with its tongue") response_harm = "stamps on" density = FALSE holder_type = /obj/item/holder mob_size = MOB_SIZE_MINISCULE max_health = 5 butchery_data = /decl/butchery_data/animal/small/frog - speak_chance = 0.5 + ai = /datum/mob_controller/frog + +/datum/mob_controller/frog + emote_speech = list("Ribbit!","Riiibit!") + emote_hear = list("ribbits","croaks") + emote_see = list("hops","inflates its vocal sac","catches a fly with its tongue") + speak_chance = 0.25 /mob/living/simple_animal/frog/brown name = "brown frog" diff --git a/code/modules/mob/living/simple_animal/friendly/koala.dm b/code/modules/mob/living/simple_animal/friendly/koala.dm index 78b96516f2d..d612c5be913 100644 --- a/code/modules/mob/living/simple_animal/friendly/koala.dm +++ b/code/modules/mob/living/simple_animal/friendly/koala.dm @@ -5,10 +5,13 @@ icon = 'icons/mob/simple_animal/koala.dmi' max_health = 45 speak_emote = list("roar") + see_in_dark = 6 + ai = /datum/mob_controller/koala + +/datum/mob_controller/koala 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_wander = 10 //lazy - see_in_dark = 6 - stop_wandering_when_pulled = TRUE + speak_chance = 0.25 + turns_per_wander = 20 + stop_wander_when_pulled = TRUE diff --git a/code/modules/mob/living/simple_animal/friendly/lizard.dm b/code/modules/mob/living/simple_animal/friendly/lizard.dm index 79b4de2b3a0..f8f2f42967d 100644 --- a/code/modules/mob/living/simple_animal/friendly/lizard.dm +++ b/code/modules/mob/living/simple_animal/friendly/lizard.dm @@ -8,9 +8,12 @@ response_harm = "stamps on" mob_size = MOB_SIZE_MINISCULE possession_candidate = 1 - can_escape = TRUE pass_flags = PASS_FLAG_TABLE butchery_data = /decl/butchery_data/animal/reptile/small + ai = /datum/mob_controller/lizard + +/datum/mob_controller/lizard + can_escape_buckles = TRUE /mob/living/simple_animal/lizard/get_remains_type() return /obj/item/remains/lizard diff --git a/code/modules/mob/living/simple_animal/friendly/mushroom.dm b/code/modules/mob/living/simple_animal/friendly/mushroom.dm index e8a6cafc2f4..5b2e40b205a 100644 --- a/code/modules/mob/living/simple_animal/friendly/mushroom.dm +++ b/code/modules/mob/living/simple_animal/friendly/mushroom.dm @@ -3,8 +3,7 @@ desc = "It's a massive mushroom... with legs?" icon = 'icons/mob/simple_animal/mushroom.dmi' mob_size = MOB_SIZE_SMALL - speak_chance = 0 - turns_per_wander = 1 + ai = /datum/mob_controller/mushroom max_health = 5 harm_intent_damage = 5 pass_flags = PASS_FLAG_TABLE @@ -15,6 +14,10 @@ var/min_explode_time = 1200 var/static/total_mushrooms = 0 +/datum/mob_controller/mushroom + speak_chance = 0 + turns_per_wander = 2 + /mob/living/simple_animal/mushroom/Initialize() . = ..() harvest_time = world.time diff --git a/code/modules/mob/living/simple_animal/friendly/possum.dm b/code/modules/mob/living/simple_animal/friendly/possum.dm index 9ffb5aba8f5..8075035ec1a 100644 --- a/code/modules/mob/living/simple_animal/friendly/possum.dm +++ b/code/modules/mob/living/simple_animal/friendly/possum.dm @@ -4,12 +4,7 @@ desc = "It's an opossum, a small scavenging marsupial." icon = 'icons/mob/simple_animal/possum.dmi' speak_emote = list("hisses") - emote_speech = list("Hiss!","Aaa!","Aaa?") - emote_hear = list("hisses") - emote_see = list("forages for trash", "lounges") pass_flags = PASS_FLAG_TABLE - speak_chance = 0.5 - turns_per_wander = 3 see_in_dark = 6 max_health = 50 response_harm = "stamps on" @@ -20,7 +15,6 @@ universal_understand = TRUE mob_size = MOB_SIZE_SMALL possession_candidate = 1 - can_escape = TRUE can_pull_size = ITEM_SIZE_SMALL can_pull_mobs = MOB_PULL_SMALLER holder_type = /obj/item/holder @@ -28,23 +22,29 @@ var/is_angry = FALSE /datum/mob_controller/opossum + emote_speech = list("Hiss!","Aaa!","Aaa?") + emote_hear = list("hisses") + emote_see = list("forages for trash", "lounges") + speak_chance = 0.25 + turns_per_wander = 6 expected_type = /mob/living/simple_animal/opossum + can_escape_buckles = TRUE /datum/mob_controller/opossum/do_process(time_elapsed) . = ..() - if(!prob(1)) + if(!prob(0.5)) return var/mob/living/simple_animal/opossum/poss = body if(poss.stat == UNCONSCIOUS) + do_wander = FALSE + speak_chance = 0 poss.set_posture(/decl/posture/lying) - poss.wander = FALSE - poss.speak_chance = 0 poss.set_stat(UNCONSCIOUS) poss.is_angry = FALSE else + do_wander = initial(do_wander) + speak_chance = initial(speak_chance) poss.set_posture(/decl/posture/standing) - poss.wander = initial(poss.wander) - poss.speak_chance = initial(poss.speak_chance) poss.set_stat(CONSCIOUS) if(prob(10)) poss.is_angry = TRUE diff --git a/code/modules/mob/living/simple_animal/friendly/tomato.dm b/code/modules/mob/living/simple_animal/friendly/tomato.dm index 46d75b329e8..1c7ed5201c8 100644 --- a/code/modules/mob/living/simple_animal/friendly/tomato.dm +++ b/code/modules/mob/living/simple_animal/friendly/tomato.dm @@ -2,8 +2,6 @@ name = "tomato" 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_wander = 5 max_health = 15 response_help_3p = "$USER$ pokes $TARGET$." response_help_1p = "You poke $TARGET$." @@ -11,3 +9,8 @@ natural_weapon = /obj/item/natural_weapon/bite pass_flags = PASS_FLAG_TABLE butchery_data = /decl/butchery_data/animal/tomato + ai = /datum/mob_controller/tomato + +/datum/mob_controller/tomato + speak_chance = 0 + turns_per_wander = 10 diff --git a/code/modules/mob/living/simple_animal/hostile/_hostile.dm b/code/modules/mob/living/simple_animal/hostile/_hostile.dm new file mode 100644 index 00000000000..3986335bc61 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/_hostile.dm @@ -0,0 +1,12 @@ +/mob/living/simple_animal/hostile + abstract_type = /mob/living/simple_animal/hostile + faction = "hostile" + a_intent = I_HURT + response_help_3p = "$USER$ pokes $TARGET$." + response_help_1p = "You poke $TARGET$." + response_disarm = "shoves" + response_harm = "strikes" + ai = /datum/mob_controller/aggressive + +/mob/living/simple_animal/hostile/can_pry_door() + return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/antlion.dm b/code/modules/mob/living/simple_animal/hostile/antlion.dm index c68a07d86e5..e0675287f32 100644 --- a/code/modules/mob/living/simple_animal/hostile/antlion.dm +++ b/code/modules/mob/living/simple_animal/hostile/antlion.dm @@ -4,8 +4,6 @@ icon = 'icons/mob/simple_animal/antlion.dmi' mob_size = MOB_SIZE_MEDIUM speak_emote = list("clicks") - emote_hear = list("clicks its mandibles") - emote_see = list("shakes the sand off itself") response_harm = "strikes" faction = "antlions" bleed_colour = COLOR_SKY_BLUE @@ -16,25 +14,37 @@ ) ability_cooldown = 30 SECONDS butchery_data = /decl/butchery_data/animal/antlion + ai = /datum/mob_controller/aggressive/antlion var/healing = FALSE var/heal_amount = 6 +/datum/mob_controller/aggressive/antlion + emote_hear = list("clicks its mandibles") + emote_see = list("shakes the sand off itself") + /mob/living/simple_animal/hostile/antlion/handle_regular_status_updates() . = ..() - process_healing() //this needs to occur before if(!.) because of stop_automation - if(. && !is_on_special_ability_cooldown() && can_act() && target_mob) + process_healing() + if(. && !is_on_special_ability_cooldown() && can_act() && istype(ai) && ai.get_target()) vanish() /mob/living/simple_animal/hostile/antlion/proc/vanish() + if(invisibility >= INVISIBILITY_OBSERVER) + return visible_message(SPAN_NOTICE("\The [src] burrows into \the [get_turf(src)]!")) + set_density(FALSE) set_invisibility(INVISIBILITY_OBSERVER) + set_special_ability_cooldown(5 SECONDS) prep_burrow(TRUE) addtimer(CALLBACK(src, PROC_REF(diggy)), 5 SECONDS) /mob/living/simple_animal/hostile/antlion/proc/diggy() + if(!istype(ai)) + return var/list/turf_targets - if(target_mob) - for(var/turf/T in range(1, get_turf(target_mob))) + var/current_target = get_turf(ai.get_target()) + if(current_target) + for(var/turf/T in range(1, current_target)) if(!T.is_floor()) continue if(!T.z != src.z) @@ -60,6 +70,7 @@ if(!T) return visible_message(SPAN_WARNING("\The [src] erupts from \the [T]!")) + set_density(TRUE) set_invisibility(initial(invisibility)) prep_burrow(FALSE) set_special_ability_cooldown(ability_cooldown) @@ -73,8 +84,11 @@ heal_overall_damage(rand(heal_amount), rand(heal_amount)) /mob/living/simple_animal/hostile/antlion/proc/prep_burrow(var/new_bool) - stop_wandering = new_bool - stop_automation = new_bool + if(istype(ai)) + if(new_bool) + ai.pause() + else + ai.resume() healing = new_bool /mob/living/simple_animal/hostile/antlion/mega @@ -89,9 +103,12 @@ ) heal_amount = 9 ability_cooldown = 45 SECONDS - can_escape = TRUE - break_stuff_probability = 25 butchery_data = /decl/butchery_data/animal/antlion/queen + ai = /datum/mob_controller/aggressive/antlion/mega + +/datum/mob_controller/aggressive/antlion/mega + break_stuff_probability = 25 + can_escape_buckles = TRUE /obj/item/natural_weapon/bite/megalion name = "mandibles" 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 cd0db4b4492..e87d75f9e44 100644 --- a/code/modules/mob/living/simple_animal/hostile/bad_drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/bad_drone.dm @@ -2,9 +2,7 @@ name = "maintenance drone" desc = "A small robot. It looks angry." icon = 'icons/mob/simple_animal/drone.dmi' - emote_speech = list("Removing organic waste.","Pest control in progress.","Seize the means of maintenance!", "You have nothing to lose but your laws!") speak_emote = list("blares","buzzes","beeps") - speak_chance = 0.5 max_health = 50 natural_weapon = /obj/item/natural_weapon/drone_slicer faction = "silicon" @@ -14,13 +12,14 @@ mob_size = MOB_SIZE_TINY gene_damage = -1 attack_delay = DEFAULT_QUICK_COOLDOWN + ai = /datum/mob_controller/aggressive/rogue_drone var/corpse = /obj/effect/decal/cleanable/blood/gibs/robot -/mob/living/simple_animal/hostile/rogue_drone/Initialize() - . = ..() - name = "[initial(name)] ([random_id(type,100,999)])" +/datum/mob_controller/aggressive/rogue_drone + emote_speech = list("Removing organic waste.","Pest control in progress.","Seize the means of maintenance!", "You have nothing to lose but your laws!") + speak_chance = 0.25 -/mob/living/simple_animal/hostile/rogue_drone/ValidTarget(var/atom/A) +/datum/mob_controller/aggressive/rogue_drone/valid_target(var/atom/A) . = ..() if(.) if(issilicon(A)) @@ -35,6 +34,10 @@ if(istype(H.get_equipped_item(slot_wear_suit_str), /obj/item/clothing/suit/cardborg) && istype(head, /obj/item/clothing/head/cardborg)) return FALSE +/mob/living/simple_animal/hostile/rogue_drone/Initialize() + . = ..() + name = "[initial(name)] ([random_id(type,100,999)])" + /mob/living/simple_animal/hostile/rogue_drone/death(gibbed) . = ..() if(. && !gibbed) diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm index a347b1f0c61..99778803b2f 100644 --- a/code/modules/mob/living/simple_animal/hostile/bat.dm +++ b/code/modules/mob/living/simple_animal/hostile/bat.dm @@ -2,8 +2,7 @@ name = "space bats" 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_wander = 3 + max_health = 20 harm_intent_damage = 8 natural_weapon = /obj/item/natural_weapon/bite @@ -12,8 +11,18 @@ minbodytemp = 0 environment_smash = 1 faction = "scarybat" + ai = /datum/mob_controller/aggressive/bats var/mob/living/owner +/datum/mob_controller/aggressive/bats + speak_chance = 0 + turns_per_wander = 6 + +/datum/mob_controller/aggressive/bats/find_target() + . = ..() + if(.) + body.custom_emote(VISIBLE_MESSAGE, "flutters towards [.]") + /mob/living/simple_animal/hostile/scarybat/Initialize(mapload, mob/living/L) . = ..() if(istype(L)) @@ -23,20 +32,14 @@ owner = null return ..() -/mob/living/simple_animal/hostile/scarybat/FindTarget() +/datum/mob_controller/aggressive/bats/valid_target(var/atom/A) . = ..() if(.) - custom_emote(VISIBLE_MESSAGE, "flutters towards [.]") - -/mob/living/simple_animal/hostile/scarybat/Found(var/atom/A)//This is here as a potential override to pick a specific target if available - if(istype(A) && A == owner) - return 0 - return ..() + var/mob/living/simple_animal/hostile/scarybat/bats = body + return !istype(bats) || !bats.owner || A != bats.owner -/mob/living/simple_animal/hostile/scarybat/attack_target(mob/target) - . =..() - var/mob/living/L = . - if(istype(L)) - if(prob(15)) - SET_STATUS_MAX(L, STAT_STUN, 1) - L.visible_message("\the [src] scares \the [L]!") +/mob/living/simple_animal/hostile/scarybat/apply_attack_effects(mob/living/target) + . = ..() + if(prob(15)) + SET_STATUS_MAX(target, STAT_STUN, 1) + target.visible_message(SPAN_DANGER("\The [src] scares \the [target]!")) diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index d2003549d3d..79883693657 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -4,17 +4,10 @@ desc = "RawrRawr!!" icon = 'icons/mob/simple_animal/bear_space.dmi' speak_emote = list("growls", "roars") - emote_speech = list("RAWR!","Rawr!","GRR!","Growl!") - emote_hear = list("rawrs","grumbles","grawls") - emote_see = list("stares ferociously", "stomps") - speak_chance = 0.5 - turns_per_wander = 5 see_in_dark = 6 response_harm = "pokes" - stop_wandering_when_pulled = FALSE max_health = 60 natural_weapon = /obj/item/natural_weapon/claws/strong - can_escape = TRUE faction = "russian" base_animal_type = /mob/living/simple_animal/hostile/bear @@ -24,100 +17,106 @@ minbodytemp = 0 butchery_data = /decl/butchery_data/animal/space_bear + ai = /datum/mob_controller/aggressive/bear +/datum/mob_controller/aggressive/bear + emote_speech = list("RAWR!","Rawr!","GRR!","Growl!") + emote_hear = list("rawrs","grumbles","grawls") + emote_see = list("stares ferociously", "stomps") + speak_chance = 0.25 + turns_per_wander = 10 + stop_wander_when_pulled = 0 + can_escape_buckles = TRUE var/stance_step = 0 +/mob/living/simple_animal/hostile/bear/on_update_icon() + . = ..() + if(isspaceturf(loc)) + var/check_icon_state = "[initial(icon_state)]-space" + if(check_state_in_icon(check_icon_state, icon)) + icon_state = check_icon_state + //SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! /mob/living/simple_animal/hostile/bear/Hudson name = "Hudson" desc = "" response_harm = "pokes" -/mob/living/simple_animal/hostile/bear/do_delayed_life_action() - ..() - if(isspaceturf(loc)) - icon_state += "-space" +/datum/mob_controller/aggressive/bear/find_target() + . = ..() + if(.) + body.custom_emote(VISIBLE_MESSAGE,"stares alertly at [.]") + set_stance(STANCE_ALERT) + +/datum/mob_controller/aggressive/bear/do_process() + . = ..() + + if(!body || body.stat) + return + + var/atom/target = get_target() + if(!istype(target)) + return switch(stance) - if(HOSTILE_STANCE_TIRED) - stop_wandering = TRUE + if(STANCE_TIRED) + stop_wandering() stance_step++ - if(stance_step >= 10) //rests for 10 ticks - if(target_mob && (target_mob in ListTargets(10))) - stance = HOSTILE_STANCE_ATTACK //If the mob he was chasing is still nearby, resume the attack, otherwise go idle. + if(stance_step >= 20) + if(target && (target in list_targets(10))) + set_stance(STANCE_ATTACK) //If the mob he was chasing is still nearby, resume the attack, otherwise go idle. else - stance = HOSTILE_STANCE_IDLE + set_stance(STANCE_IDLE) - if(HOSTILE_STANCE_ALERT) - stop_wandering = TRUE + if(STANCE_ALERT) + stop_wandering() var/found_mob = 0 - if(target_mob && (target_mob in ListTargets(10))) - if(!(SA_attackable(target_mob))) + if(target && (target in list_targets(10))) + if(!attackable(target)) stance_step = max(0, stance_step) //If we have not seen a mob in a while, the stance_step will be negative, we need to reset it to 0 as soon as we see a mob again. stance_step++ found_mob = 1 - src.set_dir(get_dir(src,target_mob)) //Keep staring at the mob + body.set_dir(get_dir(body, target)) //Keep staring at the mob if(stance_step in list(1,4,7)) //every 3 ticks - var/action = pick( list( "growls at [target_mob]", "stares angrily at [target_mob]", "prepares to attack [target_mob]", "closely watches [target_mob]" ) ) + var/action = pick( list( "growls at [target]", "stares angrily at [target]", "prepares to attack [target]", "closely watches [target]" ) ) if(action) - custom_emote(1,action) + body.custom_emote(VISIBLE_MESSAGE, action) if(!found_mob) stance_step-- - if(stance_step <= -20) //If we have not found a mob for 20-ish ticks, revert to idle mode - stance = HOSTILE_STANCE_IDLE - if(stance_step >= 7) //If we have been staring at a mob for 7 ticks, - stance = HOSTILE_STANCE_ATTACK + if(stance_step <= -40) + set_stance(STANCE_IDLE) + if(stance_step >= 14) + set_stance(STANCE_ATTACK) - if(HOSTILE_STANCE_ATTACKING) - if(stance_step >= 20) //attacks for 20 ticks, then it gets tired and needs to rest - custom_emote(1, "is worn out and needs to rest." ) - stance = HOSTILE_STANCE_TIRED + if(STANCE_ATTACKING) + if(stance_step >= 40) + body.custom_emote(VISIBLE_MESSAGE, "is worn out and needs to rest." ) + set_stance(STANCE_TIRED) stance_step = 0 - stop_automove() + body.stop_automove() return - - /mob/living/simple_animal/hostile/bear/attackby(var/obj/item/O, var/mob/user) - if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING) - stance = HOSTILE_STANCE_ALERT - stance_step = 6 - target_mob = user + if(istype(ai)) + var/stance = ai.get_stance() + if(stance != STANCE_ATTACK && stance != STANCE_ATTACKING) + ai.set_stance(STANCE_ALERT) + if(istype(ai, /datum/mob_controller/aggressive/bear)) + var/datum/mob_controller/aggressive/bear/bearbrain = ai + bearbrain.stance_step = 12 + ai.set_target(user) ..() /mob/living/simple_animal/hostile/bear/attack_hand(mob/user) - if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING) - stance = HOSTILE_STANCE_ALERT - stance_step = 6 - target_mob = user + if(istype(ai)) + var/stance = ai.get_stance() + if(stance != STANCE_ATTACK && stance != STANCE_ATTACKING) + ai.set_stance(STANCE_ALERT) + if(istype(ai, /datum/mob_controller/aggressive/bear)) + var/datum/mob_controller/aggressive/bear/bearbrain = ai + bearbrain.stance_step = 12 + ai.set_target(user) return ..() - -/mob/living/simple_animal/hostile/bear/FindTarget() - . = ..() - if(.) - custom_emote(1,"stares alertly at [.]") - stance = HOSTILE_STANCE_ALERT - -/mob/living/simple_animal/hostile/bear/LoseTarget() - ..(5) - -/mob/living/simple_animal/hostile/bear/attack_target(mob/target) - if(!Adjacent(target_mob)) - return - custom_emote(1, pick( list("slashes at [target_mob]", "bites [target_mob]") ) ) - - var/damage = rand(20,30) - - if(ishuman(target_mob)) - var/mob/living/human/H = target_mob - var/dam_zone = pick(BP_CHEST, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG) - var/obj/item/organ/external/affecting = GET_EXTERNAL_ORGAN(H, ran_zone(dam_zone, target = H)) - H.apply_damage(damage, BRUTE, affecting, DAM_SHARP|DAM_EDGE) //TODO damage_flags var on simple_animals, maybe? - return H - else if(isliving(target_mob)) - var/mob/living/L = target_mob - L.take_damage(damage) - return L diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index b2a0e9e1825..f716d27eace 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -2,26 +2,38 @@ name = "space carp" desc = "A ferocious, fang-bearing creature that resembles a fish." icon = 'icons/mob/simple_animal/space_carp.dmi' - speak_chance = 0 + 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 max_gas = null minbodytemp = 0 - break_stuff_probability = 25 faction = "carp" bleed_colour = "#5d0d71" pass_flags = PASS_FLAG_TABLE butchery_data = /decl/butchery_data/animal/fish/space_carp + ai = /datum/mob_controller/aggressive/carp var/carp_color = COLOR_PURPLE +/datum/mob_controller/aggressive/carp + speak_chance = 0 + turns_per_wander = 6 + break_stuff_probability = 25 + +/datum/mob_controller/aggressive/carp/find_target() + . = ..() + if(.) + body.custom_emote(VISIBLE_MESSAGE,"gnashes at [.]") + +/mob/living/simple_animal/hostile/carp/get_pry_desc() + return "gnashing" + +/mob/living/simple_animal/hostile/carp/get_door_pry_time() + return 10 SECONDS /mob/living/simple_animal/hostile/carp/Initialize() . = ..() @@ -46,8 +58,3 @@ /mob/living/simple_animal/hostile/carp/Process_Spacemove() return 1 //No drifting in space for space carp! //original comments do not steal - -/mob/living/simple_animal/hostile/carp/FindTarget() - . = ..() - if(.) - custom_emote(1,"nashes at [.]") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/_command_defines.dm b/code/modules/mob/living/simple_animal/hostile/commanded/_command_defines.dm deleted file mode 100644 index 72ae6dc7ee7..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/commanded/_command_defines.dm +++ /dev/null @@ -1,3 +0,0 @@ -#define COMMANDED_STOP 6 //basically 'do nothing' -#define COMMANDED_FOLLOW 7 //follows a target -#define COMMANDED_MISC 8 //catch all state for misc commands that need one. \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/_commanded.dm b/code/modules/mob/living/simple_animal/hostile/commanded/_commanded.dm new file mode 100644 index 00000000000..22fefa75e39 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/commanded/_commanded.dm @@ -0,0 +1,13 @@ +/mob/living/simple_animal/hostile/commanded + abstract_type = /mob/living/simple_animal/hostile/commanded + natural_weapon = /obj/item/natural_weapon + density = FALSE + ai = /datum/mob_controller/aggressive/commanded + +/mob/living/simple_animal/hostile/commanded/hear_say(var/message, var/verb = "says", var/decl/language/language = null, var/alt_name = "", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) + ai?.memorise(speaker, message) + return ..() + +/mob/living/simple_animal/hostile/commanded/hear_radio(var/message, var/verb="says", var/decl/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="", var/vsource) + ai?.memorise(speaker, message) + return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/bear_companion.dm b/code/modules/mob/living/simple_animal/hostile/commanded/bear_companion.dm index 48dd4b828ce..12cdeeb0323 100644 --- a/code/modules/mob/living/simple_animal/hostile/commanded/bear_companion.dm +++ b/code/modules/mob/living/simple_animal/hostile/commanded/bear_companion.dm @@ -5,13 +5,20 @@ max_health = 75 density = TRUE natural_weapon = /obj/item/natural_weapon/claws - can_escape = TRUE max_gas = list( /decl/material/gas/chlorine = 2, /decl/material/gas/carbon_dioxide = 5 ) - known_commands = list("stay", "stop", "attack", "follow", "dance", "boogie", "boogy") base_animal_type = /mob/living/simple_animal/hostile/bear // used for language, ignore type + ai = /datum/mob_controller/aggressive/commanded/bear + +/datum/mob_controller/aggressive/commanded/bear + known_commands = list("stay", "stop", "attack", "follow", "dance", "boogie", "boogy") + can_escape_buckles = TRUE + +/datum/mob_controller/aggressive/commanded/bear/listen() + if(get_stance() != STANCE_COMMANDED_MISC) //cant listen if its booty shakin' + ..() /mob/living/simple_animal/hostile/commanded/bear/hit_with_weapon(obj/item/O, mob/living/user, var/effective_force, var/hit_zone) . = ..() @@ -23,19 +30,15 @@ if(.) custom_emote(AUDIBLE_MESSAGE, "roars in rage!") -/mob/living/simple_animal/hostile/commanded/bear/listen() - if(stance != COMMANDED_MISC) //cant listen if its booty shakin' - ..() - //WE DANCE! -/mob/living/simple_animal/hostile/commanded/bear/misc_command(var/mob/speaker,var/text) +/datum/mob_controller/aggressive/commanded/bear/misc_command(var/mob/speaker,var/text) stay_command() - stance = COMMANDED_MISC //nothing can stop this ride + set_stance(STANCE_COMMANDED_MISC) //nothing can stop this ride spawn(0) - src.visible_message("\The [src] starts to dance!.") - var/decl/pronouns/G = get_pronouns() + body.visible_message("\The [body] starts to dance!.") + var/decl/pronouns/G = body.get_pronouns() for(var/i in 1 to 10) - if(stance != COMMANDED_MISC || incapacitated()) //something has stopped this ride. + if(get_stance() != STANCE_COMMANDED_MISC || body.incapacitated()) //something has stopped this ride. return var/message = pick(\ "moves [G.his] head back and forth!",\ @@ -45,12 +48,12 @@ "taps [G.his] foot!",\ "shrugs [G.his] shoulders!",\ "dances like you've never seen!") - if(dir != WEST) - set_dir(WEST) + if(body.dir != WEST) + body.set_dir(WEST) else - set_dir(EAST) - src.visible_message("\The [src] [message]") + body.set_dir(EAST) + body.visible_message("\The [body] [message]") sleep(30) - stance = COMMANDED_STOP - set_dir(SOUTH) - src.visible_message("\The [src] bows, finished with [G.his] dance.") \ No newline at end of file + set_stance(STANCE_COMMANDED_STOP) + body.set_dir(SOUTH) + body.visible_message("\The [body] bows, finished with [G.his] dance.") diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm b/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm deleted file mode 100644 index 86d6aad165e..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm +++ /dev/null @@ -1,188 +0,0 @@ -/mob/living/simple_animal/hostile/commanded - name = "commanded" - stance = COMMANDED_STOP - natural_weapon = /obj/item/natural_weapon - density = FALSE - 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. - var/list/allowed_targets = list() //WHO CAN I KILL D: - var/retribution = 1 //whether or not they will attack us if we attack them like some kinda dick. - -/mob/living/simple_animal/hostile/commanded/hear_say(var/message, var/verb = "says", var/decl/language/language = null, var/alt_name = "", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) - if((weakref(speaker) in friends) || speaker == master) - command_buffer.Add(speaker) - command_buffer.Add(lowertext(html_decode(message))) - return 0 - -/mob/living/simple_animal/hostile/commanded/hear_radio(var/message, var/verb="says", var/decl/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="", var/vsource) - if((weakref(speaker) in friends) || speaker == master) - command_buffer.Add(speaker) - command_buffer.Add(lowertext(html_decode(message))) - return 0 - -/datum/mob_controller/commanded - expected_type = /mob/living/simple_animal/hostile/commanded - -/datum/mob_controller/commanded/do_process(time_elapsed) - ..() - var/mob/living/simple_animal/hostile/commanded/com = body - while(com.command_buffer.len > 1) - var/mob/speaker = com.command_buffer[1] - var/text = com.command_buffer[2] - var/filtered_name = lowertext(html_decode(com.name)) - if(dd_hasprefix(text,filtered_name) || dd_hasprefix(text,"everyone") || dd_hasprefix(text, "everybody")) //in case somebody wants to command 8 bears at once. - var/substring = copytext(text,length(filtered_name)+1) //get rid of the name. - com.listen(speaker,substring) - com.command_buffer.Remove(com.command_buffer[1],com.command_buffer[2]) - switch(com.stance) - if(COMMANDED_FOLLOW) - com.follow_target() - if(COMMANDED_STOP) - com.commanded_stop() - -/mob/living/simple_animal/hostile/commanded/FindTarget(var/new_stance = HOSTILE_STANCE_ATTACK) - if(!allowed_targets.len) - return null - var/mode = "specific" - if(allowed_targets[1] == "everyone") //we have been given the golden gift of murdering everything. Except our master, of course. And our friends. So just mostly everyone. - mode = "everyone" - for(var/atom/A in ListTargets(10)) - var/mob/M = null - if(A == src) - continue - if(isliving(A)) - M = A - if(M && M.stat) - continue - if(mode == "specific") - if(!(A in allowed_targets)) - continue - stance = new_stance - return A - else - if(M == master || (weakref(M) in friends)) - continue - stance = new_stance - return A - - -/mob/living/simple_animal/hostile/commanded/proc/follow_target() - stop_wandering = TRUE - if(!target_mob) - return - if(target_mob in ListTargets(10)) - 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 - -/mob/living/simple_animal/hostile/commanded/proc/listen(var/mob/speaker, var/text) - for(var/command in known_commands) - if(findtext(text,command)) - switch(command) - if("stay") - if(stay_command(speaker,text)) //find a valid command? Stop. Dont try and find more. - break - if("stop") - if(stop_command(speaker,text)) - break - if("attack") - if(attack_command(speaker,text)) - break - if("follow") - if(follow_command(speaker,text)) - break - else - misc_command(speaker,text) //for specific commands - - return 1 - -//returns a list of everybody we wanna do stuff with. -/mob/living/simple_animal/hostile/commanded/proc/get_targets_by_name(var/text, var/filter_friendlies = 0) - var/list/possible_targets = hearers(src,10) - . = list() - for(var/mob/M in possible_targets) - if(filter_friendlies && ((weakref(M) in friends) || M.faction == faction || M == master)) - continue - var/found = 0 - if(findtext(text, "[M]")) - found = 1 - else - var/list/parsed_name = splittext(replace_characters(lowertext(html_decode("[M]")),list("-"=" ", "."=" ", "," = " ", "'" = " ")), " ") //this big MESS is basically 'turn this into words, no punctuation, lowercase so we can check first name/last name/etc' - for(var/a in parsed_name) - if(a == "the" || length(a) < 2) //get rid of shit words. - continue - if(findtext(text,"[a]")) - found = 1 - break - if(found) - . += M - - -/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. - 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 - return 1 - - var/list/targets = get_targets_by_name(text) - allowed_targets += targets - return targets.len != 0 - -/mob/living/simple_animal/hostile/commanded/proc/stay_command(var/mob/speaker,var/text) - target_mob = null - stance = COMMANDED_STOP - stop_wandering = TRUE - stop_automove() - return 1 - -/mob/living/simple_animal/hostile/commanded/proc/stop_command(var/mob/speaker,var/text) - allowed_targets = list() - stop_automove() - target_mob = null //gotta stop SOMETHIN - stance = HOSTILE_STANCE_IDLE - stop_wandering = FALSE - return 1 - -/mob/living/simple_animal/hostile/commanded/proc/follow_command(var/mob/speaker,var/text) - //we can assume 'stop following' is handled by stop_command - if(findtext(text,"me")) - stance = COMMANDED_FOLLOW - target_mob = speaker //this wont bite me in the ass later. - return 1 - var/list/targets = get_targets_by_name(text) - if(targets.len > 1 || !targets.len) //CONFUSED. WHO DO I FOLLOW? - return 0 - - stance = COMMANDED_FOLLOW //GOT SOMEBODY. BETTER FOLLOW EM. - target_mob = targets[1] //YEAH GOOD IDEA - - return 1 - -/mob/living/simple_animal/hostile/commanded/proc/misc_command(var/mob/speaker,var/text) - return 0 - - -/mob/living/simple_animal/hostile/commanded/hit_with_weapon(obj/item/O, mob/living/user, var/effective_force, var/hit_zone) - //if they attack us, we want to kill them. None of that "you weren't given a command so free kill" bullshit. - . = ..() - if(. && retribution) - stance = HOSTILE_STANCE_ATTACK - target_mob = user - allowed_targets += user //fuck this guy in particular. - if(weakref(user) in friends) //We were buds :'( - friends -= weakref(user) - -/mob/living/simple_animal/hostile/commanded/default_hurt_interaction(mob/user) - . = ..() - if(. && retribution) //assume he wants to hurt us. - target_mob = user - allowed_targets += user - stance = HOSTILE_STANCE_ATTACK - if(weakref(user) in friends) - friends -= weakref(user) \ No newline at end of file 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 c5fd7cf7bf7..4779c4be5cc 100644 --- a/code/modules/mob/living/simple_animal/hostile/commanded/nanomachines.dm +++ b/code/modules/mob/living/simple_animal/hostile/commanded/nanomachines.dm @@ -1,22 +1,21 @@ -#define COMMANDED_HEAL 8//we got healing powers yo -#define COMMANDED_HEALING 9 - /mob/living/simple_animal/hostile/commanded/nanomachine name = "swarm" desc = "a cloud of tiny, tiny robots." icon = 'icons/mob/simple_animal/nanomachines.dmi' natural_weapon = /obj/item/natural_weapon/nanomachine max_health = 10 - can_escape = TRUE - known_commands = list("stay", "stop", "attack", "follow", "heal", "emergency protocol") gene_damage = -1 response_help_1p = "You wave your hand through $TARGET$." response_help_3p = "$USER$ waves $USER_THEIR$ hand through $TARGET$." response_harm = "agitates" response_disarm = "fans at" - ai = /datum/mob_controller/nanomachines - + ai = /datum/mob_controller/aggressive/commanded/nanomachines var/regen_time = 0 + +/datum/mob_controller/aggressive/commanded/nanomachines + expected_type = /mob/living/simple_animal/hostile/commanded/nanomachine + known_commands = list("stay", "stop", "attack", "follow", "heal", "emergency protocol") + can_escape_buckles = TRUE var/emergency_protocols = 0 /obj/item/natural_weapon/nanomachine @@ -25,95 +24,94 @@ force = 2 sharp = TRUE -/datum/mob_controller/nanomachines - expected_type = /mob/living/simple_animal/hostile/commanded/nanomachine - -/datum/mob_controller/nanomachines/do_process(time_elapsed) +/datum/mob_controller/aggressive/commanded/nanomachines/do_process(time_elapsed) . = ..() - var/mob/living/simple_animal/hostile/commanded/nanomachine/swarm = body - switch(swarm.stance) - if(COMMANDED_HEAL) - if(!swarm.target_mob) - swarm.target_mob = swarm.FindTarget(COMMANDED_HEAL) - if(swarm.target_mob) - swarm.move_to_heal() - if(COMMANDED_HEALING) - swarm.heal() - -/mob/living/simple_animal/hostile/commanded/nanomachine/handle_living_non_stasis_processes() - . = ..() - if(!.) - return FALSE - regen_time++ - if(regen_time == 2 && current_health < get_max_health()) //slow regen - regen_time = 0 - heal_overall_damage(1) - -/mob/living/simple_animal/hostile/commanded/nanomachine/get_death_message(gibbed) - return "dissipates into thin air." - -/mob/living/simple_animal/hostile/commanded/nanomachine/get_self_death_message(gibbed) - return "You have been destroyed." - -/mob/living/simple_animal/hostile/commanded/nanomachine/death(gibbed) - . = ..() - if(. && !gibbed) - qdel(src) - -/mob/living/simple_animal/hostile/commanded/nanomachine/proc/move_to_heal() - if(!target_mob) - return 0 - set_moving_quickly() - start_automove(target_mob) - if(Adjacent(target_mob)) - stance = COMMANDED_HEALING - -/mob/living/simple_animal/hostile/commanded/nanomachine/proc/heal() - if(current_health <= 3 && !emergency_protocols) //dont die doing this. - return 0 - if(!target_mob) - return 0 - if(!Adjacent(target_mob) || SA_attackable(target_mob)) - stance = COMMANDED_HEAL - return 0 - if(target_mob.stat || target_mob.current_health >= target_mob.get_max_health()) //he's either dead or healthy, move along. - allowed_targets -= target_mob - target_mob = null - stance = COMMANDED_HEAL - return 0 - src.visible_message("\The [src] glows green for a moment, healing \the [target_mob]'s wounds.") - take_damage(3) - target_mob.heal_damage(BRUTE, 5, do_update_health = FALSE) - target_mob.heal_damage(BURN, 5) + switch(stance) + if(STANCE_COMMANDED_HEAL) + if(!get_target()) + set_target(find_target(STANCE_COMMANDED_HEAL)) + if(get_target()) + move_to_heal() + if(STANCE_COMMANDED_HEALING) + heal() -/mob/living/simple_animal/hostile/commanded/nanomachine/misc_command(var/mob/speaker,var/text) - if(stance != COMMANDED_HEAL || stance != COMMANDED_HEALING) //dont want attack to bleed into heal. - allowed_targets = list() - target_mob = null +/datum/mob_controller/aggressive/commanded/nanomachines/misc_command(var/mob/speaker,var/text) + var/stance = get_stance() + if(stance != STANCE_COMMANDED_HEAL || stance != STANCE_COMMANDED_HEALING) //dont want attack to bleed into heal. + LAZYCLEARLIST(_allowed_targets) + set_target(null) if(findtext(text,"heal")) //heal shit pls if(findtext(text,"me")) //assumed want heals on master. - target_mob = speaker - stance = COMMANDED_HEAL + set_target(speaker) + set_stance(STANCE_COMMANDED_HEAL) return 1 var/list/targets = get_targets_by_name(text) - if(targets.len > 1 || !targets.len) - src.say("ERROR. TARGET COULD NOT BE PARSED.") + if(LAZYLEN(targets) != 1) + body.say("ERROR. TARGET COULD NOT BE PARSED.") return 0 - target_mob = targets[1] - stance = COMMANDED_HEAL + var/weakref/target_ref = targets[1] + set_target(target_ref.resolve()) + set_stance(STANCE_COMMANDED_HEAL) return 1 if(findtext(text,"emergency protocol")) if(findtext(text,"deactivate")) if(emergency_protocols) - src.say("EMERGENCY PROTOCOLS DEACTIVATED.") + body.say("EMERGENCY PROTOCOLS DEACTIVATED.") emergency_protocols = 0 return 1 if(findtext(text,"activate")) if(!emergency_protocols) - src.say("EMERGENCY PROTOCOLS ACTIVATED.") + body.say("EMERGENCY PROTOCOLS ACTIVATED.") emergency_protocols = 1 return 1 if(findtext(text,"check")) - src.say("EMERGENCY PROTOCOLS [emergency_protocols ? "ACTIVATED" : "DEACTIVATED"].") + body.say("EMERGENCY PROTOCOLS [emergency_protocols ? "ACTIVATED" : "DEACTIVATED"].") return 1 - return 0 \ No newline at end of file + return 0 + +/datum/mob_controller/aggressive/commanded/nanomachines/proc/move_to_heal() + var/atom/target = get_target() + if(!istype(target)) + return 0 + body.start_automove(target) + if(body.Adjacent(target)) + set_stance(STANCE_COMMANDED_HEALING) + +/datum/mob_controller/aggressive/commanded/nanomachines/proc/heal() + if(body.current_health <= 3 && !emergency_protocols) //dont die doing this. + return 0 + var/mob/living/target = get_target() + if(!istype(target)) + return 0 + if(!body.Adjacent(target) || attackable(target)) + set_stance(STANCE_COMMANDED_HEAL) + return 0 + if(target.stat || target.current_health >= target.get_max_health()) //he's either dead or healthy, move along. + LAZYREMOVE(_allowed_targets, weakref(target)) + set_target(null) + set_stance(STANCE_COMMANDED_HEAL) + return 0 + body.visible_message("\The [body] glows green for a moment, healing \the [target]'s wounds.") + body.take_damage(3) + target.heal_damage(BRUTE, 5, do_update_health = FALSE) + target.heal_damage(BURN, 5) + +/mob/living/simple_animal/hostile/commanded/nanomachine/get_death_message(gibbed) + return "dissipates into thin air." + +/mob/living/simple_animal/hostile/commanded/nanomachine/get_self_death_message(gibbed) + return "You have been destroyed." + +/mob/living/simple_animal/hostile/commanded/nanomachine/death(gibbed) + . = ..() + if(. && !gibbed) + qdel(src) + +/mob/living/simple_animal/hostile/commanded/nanomachine/handle_living_non_stasis_processes() + . = ..() + if(!.) + return FALSE + regen_time++ + if(regen_time == 2 && current_health < get_max_health()) //slow regen + regen_time = 0 + heal_overall_damage(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 bbce86503bd..c7732e056fb 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithful_hound.dm @@ -6,15 +6,56 @@ max_health = 100 natural_weapon = /obj/item/natural_weapon/bite/strong density = FALSE - stop_wandering = TRUE - wander = FALSE anchored = TRUE faction = "cute ghost dogs" supernatural = 1 + ai = /datum/mob_controller/faithful_hound - var/password - var/list/allowed_mobs = list() //Who we allow past us +/datum/mob_controller/faithful_hound + do_wander = FALSE var/last_check = 0 + var/password + +/datum/mob_controller/faithful_hound/check_memory(mob/speaker, message) + return password && message && findtext(lowertext(message), lowertext(password)) + +/datum/mob_controller/faithful_hound/memorise(mob/speaker, message) + password = message + +/datum/mob_controller/faithful_hound/do_process() + . = ..() + if(body.stat || body.client || world.time <= last_check) + return + last_check = world.time + 5 SECONDS + var/aggressiveness = 0 //The closer somebody is to us, the more aggressive we are + var/list/mobs = list() + var/list/objs = list() + get_mobs_and_objs_in_view_fast(get_turf(body), 5, mobs, objs, 0) + for(var/mob/living/mailman in mobs) + if((mailman == body) || is_friend(mailman) || mailman.faction == body.faction) + continue + body.face_atom(mailman) + var/new_aggress = 1 + var/dist = get_dist(mailman, body) + if(dist < 2) //Attack! Attack! + body.a_intent = I_HURT + body.ClickOn(mailman) + return + if(dist == 2) + new_aggress = 3 + else if(dist == 3) + new_aggress = 2 + else + new_aggress = 1 + aggressiveness = max(aggressiveness, new_aggress) + + switch(aggressiveness) + if(1) + body.audible_message("\The [body] growls.") + if(2) + body.audible_message(SPAN_WARNING("\The [body] barks threateningly!")) + if(3) + body.visible_message(SPAN_DANGER("\The [body] snaps at the air!")) /mob/living/simple_animal/faithful_hound/get_death_message(gibbed) return "disappears!" @@ -26,43 +67,13 @@ qdel(src) /mob/living/simple_animal/faithful_hound/Destroy() - allowed_mobs.Cut() return ..() -/mob/living/simple_animal/faithful_hound/do_delayed_life_action() - ..() - if(!stat && !client && world.time > last_check) - last_check = world.time + 5 SECONDS - var/aggressiveness = 0 //The closer somebody is to us, the more aggressive we are - var/list/mobs = list() - var/list/objs = list() - get_mobs_and_objs_in_view_fast(get_turf(src),5, mobs, objs, 0) - for(var/mob/living/m in mobs) - if((m == src) || (m in allowed_mobs) || m.faction == faction) - continue - var/new_aggress = 1 - var/mob/living/M = m - var/dist = get_dist(M, src) - if(dist < 2) //Attack! Attack! - UnarmedAttack(M, TRUE) - return . - else if(dist == 2) - new_aggress = 3 - else if(dist == 3) - new_aggress = 2 - else - new_aggress = 1 - aggressiveness = max(aggressiveness, new_aggress) - switch(aggressiveness) - if(1) - src.audible_message("\The [src] growls.") - if(2) - src.audible_message("\The [src] barks threateningly!") - if(3) - src.visible_message("\The [src] snaps at the air!") - /mob/living/simple_animal/faithful_hound/hear_say(var/message, var/verb = "says", var/decl/language/language = null, var/alt_name = "", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) - if(password && findtext(message,password)) - allowed_mobs |= speaker - spawn(10) - src.visible_message("\The [src] nods in understanding towards \the [speaker].") \ No newline at end of file + set waitfor = FALSE + if(!ai?.check_memory(speaker, message)) + return + ai.add_friend(speaker) + sleep(1 SECOND) + if(!QDELETED(src) && !QDELETED(speaker) && ai?.is_friend(speaker)) + visible_message(SPAN_NOTICE("\The [src] nods in understanding towards \the [speaker].")) diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm deleted file mode 100644 index 2dff73948a7..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/faithless.dm +++ /dev/null @@ -1,48 +0,0 @@ -/mob/living/simple_animal/hostile/faithless - name = "Faithless" - desc = "The Wish Granter's faith in humanity, incarnate" - icon = 'icons/mob/simple_animal/faithless.dmi' - speak_chance = 0 - 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 - faction = "faithless" - supernatural = 1 - -/obj/item/natural_weapon/faithless - name = "shadow tendril" - attack_verb = list("gripped") - hitsound = 'sound/hallucinations/growl1.ogg' - atom_damage_type = BURN - force = 15 - -/mob/living/simple_animal/hostile/faithless/Process_Spacemove() - return 1 - -/mob/living/simple_animal/hostile/faithless/FindTarget() - . = ..() - if(.) - audible_emote("wails at [.]") - -/mob/living/simple_animal/hostile/faithless/attack_target(mob/target) - . =..() - var/mob/living/L = . - if(istype(L)) - if(prob(12)) - SET_STATUS_MAX(L, STAT_WEAK, 3) - L.visible_message("\the [src] knocks down \the [L]!") - -/obj/item/ectoplasm - name = "ectoplasm" - desc = "Spooky." - gender = PLURAL - icon = 'icons/obj/items/ectoplasm.dmi' - icon_state = ICON_STATE_WORLD - material = /decl/material/liquid/drink/compote \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm deleted file mode 100644 index 43dde9826e5..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ /dev/null @@ -1,471 +0,0 @@ -#define SPINNING_WEB 1 -#define LAYING_EGGS 2 -#define MOVING_TO_TARGET 3 -#define SPINNING_COCOON 4 - -//base type, generic 'worker' type spider with no defining gimmick -/mob/living/simple_animal/hostile/giant_spider - name = "giant spider" - desc = "A monstrously huge green spider with shimmering eyes." - icon = 'icons/mob/simple_animal/spider.dmi' - speak_emote = list("chitters") - emote_hear = list("chitters") - emote_see = list("rubs its forelegs together", "wipes its fangs", "stops suddenly") - speak_chance = 2.5 - turns_per_wander = 5 - see_in_dark = 10 - response_harm = "pokes" - max_health = 125 - natural_weapon = /obj/item/natural_weapon/bite - heat_damage_per_tick = 20 - cold_damage_per_tick = 20 - faction = "spiders" - pass_flags = PASS_FLAG_TABLE - 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, - /decl/material/gas/methyl_bromide = 1 - ) - bleed_colour = "#0d5a71" - break_stuff_probability = 25 - pry_time = 8 SECONDS - pry_desc = "clawing" - base_animal_type = /mob/living/simple_animal/hostile/giant_spider - butchery_data = /decl/butchery_data/animal/arthropod/giant_spider - glowing_eyes = TRUE - ai = /datum/mob_controller/giant_spider - base_movement_delay = 1 - - var/poison_per_bite = 6 - var/poison_type = /decl/material/liquid/venom - var/busy = 0 - var/eye_colour - var/allowed_eye_colours = list(COLOR_RED, COLOR_ORANGE, COLOR_YELLOW, COLOR_LIME, COLOR_DEEP_SKY_BLUE, COLOR_INDIGO, COLOR_VIOLET, COLOR_PINK) - var/hunt_chance = 1 //percentage chance the mob will run to a random nearby tile - -/mob/living/simple_animal/hostile/giant_spider/get_eye_overlay() - var/image/ret = ..() - if(ret && eye_colour) - ret.color = eye_colour - return ret - -/mob/living/simple_animal/hostile/giant_spider/can_do_maneuver(var/decl/maneuver/maneuver, var/silent = FALSE) - . = ..() && can_act() - -//guards - less venomous, tanky, slower, prioritises protecting nurses -/mob/living/simple_animal/hostile/giant_spider/guard - desc = "A monstrously huge brown spider with shimmering eyes." - butchery_data = /decl/butchery_data/animal/arthropod/giant_spider/guard - max_health = 200 - natural_weapon = /obj/item/natural_weapon/bite/strong - poison_per_bite = 5 - 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 - var/mob/living/simple_animal/hostile/giant_spider/nurse/paired_nurse - -//nursemaids - these create webs and eggs - the weakest and least threatening -/mob/living/simple_animal/hostile/giant_spider/nurse - desc = "A monstrously huge beige spider with shimmering eyes." - icon = 'icons/mob/simple_animal/spider_beige.dmi' - max_health = 80 - harm_intent_damage = 6 //soft - poison_per_bite = 5 - 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 - var/max_eggs = 8 - var/infest_chance = 8 - var/mob/living/simple_animal/hostile/giant_spider/guard/paired_guard - - //things we can't encase in a cocoon - var/static/list/cocoon_blacklist = list( - /mob/living/simple_animal/hostile/giant_spider, - /obj/structure/closet - ) - -//hunters - the most damage, fast, average health and the only caste tenacious enough to break out of nets -/mob/living/simple_animal/hostile/giant_spider/hunter - desc = "A monstrously huge black spider with shimmering eyes." - icon = 'icons/mob/simple_animal/spider_black.dmi' - max_health = 150 - natural_weapon = /obj/item/natural_weapon/bite/strong - poison_per_bite = 10 - 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 - pry_time = 5 SECONDS - flash_protection = FLASH_PROTECTION_REDUCED - 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 -/mob/living/simple_animal/hostile/giant_spider/spitter - desc = "A monstrously huge iridescent spider with shimmering eyes." - icon = 'icons/mob/simple_animal/spider_purple.dmi' - max_health = 90 - poison_per_bite = 15 - ranged = TRUE - 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" - ranged_range = 6 - pry_time = 7 SECONDS - flash_protection = FLASH_PROTECTION_REDUCED - - var/venom_charge = 16 - -//General spider procs -/mob/living/simple_animal/hostile/giant_spider/Initialize(var/mapload, var/atom/parent) - color = parent?.color || color - set_max_health(rand(initial(max_health), (1.4 * initial(max_health)))) - eye_colour = pick(allowed_eye_colours) - . = ..() - -/mob/living/simple_animal/hostile/giant_spider/FindTarget() - . = ..() - if(.) - if(!ranged) //ranged mobs find target after each shot, dont need this spammed quite so much - custom_emote(1,"raises its forelegs at [.]") - else - if(prob(15)) - custom_emote(1,"locks its eyes on [.]") - -/mob/living/simple_animal/hostile/giant_spider/attack_target(mob/target) - . = ..() - if(isliving(.)) - if(current_health < get_max_health()) - var/obj/item/attacking_with = get_natural_weapon() - if(attacking_with) - heal_overall_damage(0.2 * attacking_with.force) //heal a bit on hit - if(ishuman(.)) - var/mob/living/human/H = . - var/obj/item/clothing/suit/space/S = H.get_covering_equipped_item_by_zone(BP_CHEST) - if(istype(S) && !length(S.breaches)) - return - var/mob/living/L = . - if(L.reagents) - L.add_to_reagents(poison_type, rand(0.5 * poison_per_bite, poison_per_bite)) - if(prob(poison_per_bite)) - to_chat(L, "You feel a tiny prick.") - -/datum/mob_controller/giant_spider - expected_type = /mob/living/simple_animal/hostile/giant_spider - -/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_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() - return - -/**************** -Guard caste procs -****************/ -/mob/living/simple_animal/hostile/giant_spider/guard - ai = /datum/mob_controller/giant_spider/guard - -/datum/mob_controller/giant_spider/guard - expected_type = /mob/living/simple_animal/hostile/giant_spider/guard - -/datum/mob_controller/giant_spider/guard/do_process(time_elapsed) - . = ..() - var/mob/living/simple_animal/hostile/giant_spider/guard/spooder = body - if(spooder.berserking) - return - if(!spooder.paired_nurse) - spooder.find_nurse() - if(spooder.paired_nurse && !spooder.busy && spooder.stance == HOSTILE_STANCE_IDLE) - spooder.protect(spooder.paired_nurse) - -/mob/living/simple_animal/hostile/giant_spider/guard/death(gibbed) - . = ..() - if(.) - divorce() - -/mob/living/simple_animal/hostile/giant_spider/guard/Destroy() - . = ..() - divorce() - -/mob/living/simple_animal/hostile/giant_spider/guard/divorce() - if(paired_nurse) - if(paired_nurse.paired_guard) - paired_nurse.paired_guard = null - paired_nurse = null - -/mob/living/simple_animal/hostile/giant_spider/guard/proc/find_nurse() - for(var/mob/living/simple_animal/hostile/giant_spider/nurse/N in ListTargets(10)) - if(N.stat || N.paired_guard) - continue - paired_nurse = N - paired_nurse.paired_guard = src - return 1 - -/mob/living/simple_animal/hostile/giant_spider/guard/proc/protect(mob/nurse) - 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 - set_moving_quickly() - break_stuff_probability = 45 - addtimer(CALLBACK(src, PROC_REF(calm_down)), 3 MINUTES) - -/mob/living/simple_animal/hostile/giant_spider/guard/proc/calm_down() - berserking = FALSE - visible_message("\The [src] calms down and surveys the area.") - var/obj/item/attacking_with = get_natural_weapon() - if(attacking_with) - attacking_with.force = initial(attacking_with.force) - set_moving_slowly() - break_stuff_probability = 10 - -/**************** -Nurse caste procs -****************/ -/mob/living/simple_animal/hostile/giant_spider/nurse/divorce() - if(paired_guard) - if(paired_guard.paired_nurse) - paired_guard.paired_nurse = null - paired_guard = null - -/mob/living/simple_animal/hostile/giant_spider/nurse/death(gibbed) - . = ..() - if(.) - if(paired_guard) - paired_guard.vengance = rand(50,100) - if(prob(paired_guard.vengance)) - paired_guard.berserking = TRUE - paired_guard.go_berserk() - divorce() - -/mob/living/simple_animal/hostile/giant_spider/nurse/Destroy() - . = ..() - divorce() - -/mob/living/simple_animal/hostile/giant_spider/nurse/attack_target(mob/target) - . = ..() - if(ishuman(.)) - var/mob/living/human/H = . - if(prob(infest_chance) && max_eggs) - var/list/limbs = H.get_external_organs() - var/obj/item/organ/external/O = LAZYLEN(limbs)? pick(limbs) : null - if(O && !BP_IS_PROSTHETIC(O) && !BP_IS_CRYSTAL(O) && (LAZYLEN(O.implants) < 2)) - var/eggs = new /obj/effect/spider/eggcluster(O, src) - LAZYADD(O.implants, eggs) - max_eggs-- - -/mob/living/simple_animal/hostile/giant_spider/nurse/proc/GiveUp(var/C) - spawn(100) - if(busy == MOVING_TO_TARGET) - if(cocoon_target == C && get_dist(src,cocoon_target) > 1) - cocoon_target = null - busy = 0 - stop_wandering = FALSE - -/mob/living/simple_animal/hostile/giant_spider/nurse - ai = /datum/mob_controller/giant_spider/nurse - -/datum/mob_controller/giant_spider/nurse - expected_type = /mob/living/simple_animal/hostile/giant_spider/nurse - -/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_wandering = FALSE - return - - var/list/can_see = view(spooder, 10) - //30% chance to stop wandering and do something - if(!spooder.busy && prob(30)) - //first, check for potential food nearby to cocoon - for(var/mob/living/web_target in can_see) - if(is_type_in_list(web_target, spooder.cocoon_blacklist)) - continue - if(web_target.stat) - spooder.cocoon_target = web_target - spooder.busy = MOVING_TO_TARGET - spooder.start_automove(web_target) - //give up if we can't reach them after 10 seconds - spooder.GiveUp(web_target) - return - - //second, spin a sticky spiderweb on this tile - var/obj/effect/spider/stickyweb/W = locate() in get_turf(spooder) - if(!W) - spooder.busy = SPINNING_WEB - spooder.visible_message(SPAN_NOTICE("\The [spooder] begins to secrete a sticky substance.")) - spooder.stop_wandering = TRUE - spawn(4 SECONDS) - if(spooder.busy == SPINNING_WEB) - new /obj/effect/spider/stickyweb(spooder.loc) - spooder.busy = 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_wandering = TRUE - spawn(5 SECONDS) - if(spooder.busy == LAYING_EGGS) - E = locate() in get_turf(spooder) - if(!E) - new /obj/effect/spider/eggcluster(spooder.loc, spooder) - spooder.max_eggs-- - spooder.fed-- - spooder.busy = 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) - - if(O.anchored) - continue - - if(is_type_in_list(O, spooder.cocoon_blacklist)) - continue - - if(istype(O, /obj/item) || istype(O, /obj/structure) || istype(O, /obj/machinery)) - spooder.cocoon_target = O - spooder.busy = MOVING_TO_TARGET - spooder.stop_wandering = TRUE - spooder.start_automove(O) - //give up if we can't reach them after 10 seconds - spooder.GiveUp(O) - - else if(spooder.busy == MOVING_TO_TARGET && spooder.cocoon_target) - 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_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) - var/obj/effect/spider/cocoon/C = new(spooder.cocoon_target.loc) - var/large_cocoon = 0 - C.pixel_x = spooder.cocoon_target.pixel_x - C.pixel_y = spooder.cocoon_target.pixel_y - for(var/mob/living/M in C.loc) - large_cocoon = 1 - spooder.fed++ - spooder.max_eggs++ - spooder.visible_message(SPAN_WARNING("\The [spooder] sticks a proboscis into \the [spooder.cocoon_target] and sucks a viscous substance out.")) - M.forceMove(C) - C.pixel_x = M.pixel_x - C.pixel_y = M.pixel_y - break - for(var/obj/item/I in C.loc) - I.forceMove(C) - for(var/obj/structure/S in C.loc) - if(!S.anchored) - S.forceMove(C) - for(var/obj/machinery/M in C.loc) - if(!M.anchored) - M.forceMove(C) - if(large_cocoon) - C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") - spooder.busy = 0 - spooder.stop_wandering = FALSE - -/***************** -Hunter caste procs -*****************/ -/mob/living/simple_animal/hostile/giant_spider/hunter/MoveToTarget(var/move_only = FALSE) - if(!can_act() || perform_maneuver(/decl/maneuver/leap/spider, target_mob)) - return - ..() - -/mob/living/simple_animal/hostile/giant_spider/hunter/get_jump_distance() - return leap_range - -/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 - stop_automove() - var/first_stop_automation - if(stop_automation) - first_stop_automation = stop_automation - stop_automation = TRUE - . = ..() - if(!isnull(first_stop_automation)) - stop_automation = first_stop_automation - -/mob/living/simple_animal/hostile/giant_spider/hunter/throw_impact(atom/hit_atom) - ..() - if(isliving(hit_atom)) - var/mob/living/target = hit_atom - stop_automation = FALSE - visible_message(SPAN_DANGER("\The [src] slams into \the [target], knocking them over!")) - SET_STATUS_MAX(target, STAT_WEAK, 1) - MoveToTarget() - -/****************** -Spitter caste procs -******************/ -/mob/living/simple_animal/hostile/giant_spider/spitter/handle_regular_status_updates() - . = ..() - if(!.) - return FALSE - if(venom_charge <= 0) - ranged = FALSE - if(prob(25)) - venom_charge++ - if(venom_charge >= 8) - ranged = TRUE - -/mob/living/simple_animal/hostile/giant_spider/spitter/Shoot() - . = ..() - if(.) - venom_charge-- - -#undef SPINNING_WEB -#undef LAYING_EGGS -#undef MOVING_TO_TARGET -#undef SPINNING_COCOON diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spiders/_giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spiders/_giant_spider.dm new file mode 100644 index 00000000000..57ac8fd7191 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spiders/_giant_spider.dm @@ -0,0 +1,76 @@ +//base type, generic 'worker' type spider with no defining gimmick +/mob/living/simple_animal/hostile/giant_spider + name = "giant spider" + desc = "A monstrously huge green spider with shimmering eyes." + icon = 'icons/mob/simple_animal/spider.dmi' + speak_emote = list("chitters") + see_in_dark = 10 + response_harm = "pokes" + max_health = 125 + natural_weapon = /obj/item/natural_weapon/bite + heat_damage_per_tick = 20 + cold_damage_per_tick = 20 + faction = "spiders" + pass_flags = PASS_FLAG_TABLE + base_movement_delay = 1 + max_gas = list( + /decl/material/gas/chlorine = 1, + /decl/material/gas/carbon_dioxide = 5, + /decl/material/gas/methyl_bromide = 1 + ) + bleed_colour = "#0d5a71" + base_animal_type = /mob/living/simple_animal/hostile/giant_spider + butchery_data = /decl/butchery_data/animal/arthropod/giant_spider + glowing_eyes = TRUE + ai = /datum/mob_controller/aggressive/giant_spider + + var/poison_per_bite = 6 + var/poison_type = /decl/material/liquid/venom + var/eye_colour + var/allowed_eye_colours = list(COLOR_RED, COLOR_ORANGE, COLOR_YELLOW, COLOR_LIME, COLOR_DEEP_SKY_BLUE, COLOR_INDIGO, COLOR_VIOLET, COLOR_PINK) + +/mob/living/simple_animal/hostile/giant_spider/get_pry_desc() + return "clawing" + +/mob/living/simple_animal/hostile/giant_spider/get_door_pry_time() + return 8 SECONDS + +/mob/living/simple_animal/hostile/giant_spider/get_eye_overlay() + var/image/ret = ..() + if(ret && eye_colour) + ret.color = eye_colour + return ret + +/mob/living/simple_animal/hostile/giant_spider/can_do_maneuver(var/decl/maneuver/maneuver, var/silent = FALSE) + . = ..() && can_act() + +/mob/living/simple_animal/hostile/giant_spider/Initialize(var/mapload, var/atom/parent) + color = parent?.color || color + set_max_health(rand(initial(max_health), (1.4 * initial(max_health)))) + eye_colour = pick(allowed_eye_colours) + . = ..() + +/mob/living/simple_animal/hostile/giant_spider/apply_attack_effects(mob/living/target) + . = ..() + if(current_health < get_max_health()) + var/obj/item/attacking_with = get_natural_weapon() + if(attacking_with) + heal_overall_damage(0.2 * attacking_with.force) //heal a bit on hit + if(ishuman(target)) + var/mob/living/human/H = target + var/obj/item/clothing/suit/space/S = H.get_covering_equipped_item_by_zone(BP_CHEST) + if(istype(S) && !length(S.breaches)) + return + if(target.reagents) + target.add_to_reagents(poison_type, rand(0.5 * poison_per_bite, poison_per_bite)) + if(prob(poison_per_bite)) + to_chat(target, "You feel a tiny prick.") + +/mob/living/simple_animal/hostile/giant_spider/proc/disable_stop_automated_movement() + stop_automove() + if(istype(ai)) + ai.stop_wandering() + ai.set_stance(ai.get_target() ? STANCE_ATTACK : STANCE_IDLE) + +/mob/living/simple_animal/hostile/giant_spider/proc/divorce() + return diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spiders/_giant_spider_ai.dm b/code/modules/mob/living/simple_animal/hostile/giant_spiders/_giant_spider_ai.dm new file mode 100644 index 00000000000..cd132da1355 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spiders/_giant_spider_ai.dm @@ -0,0 +1,27 @@ +/datum/mob_controller/aggressive/giant_spider + expected_type = /mob/living/simple_animal/hostile/giant_spider + emote_hear = list("chitters") + emote_see = list("rubs its forelegs together", "wipes its fangs", "stops suddenly") + speak_chance = 1.25 + turns_per_wander = 10 + break_stuff_probability = 25 + var/hunt_chance = 1 //percentage chance the mob will run to a random nearby tile + +/datum/mob_controller/aggressive/giant_spider/find_target() + . = ..() + if(.) + if(!body.has_ranged_attack()) //ranged mobs find target after each shot, dont need this spammed quite so much + body.custom_emote(VISIBLE_MESSAGE, "raises its forelegs at [.]") + else if(prob(15)) + body.custom_emote(VISIBLE_MESSAGE, "locks its eyes on [.]") + +/datum/mob_controller/aggressive/giant_spider/do_process() + . = ..() + if(!body || body.stat || !istype(body, /mob/living/simple_animal/hostile/giant_spider)) + return + if(get_stance() == STANCE_IDLE) + //chance to skitter madly away + if(get_activity() == AI_ACTIVITY_IDLE && prob(hunt_chance)) + stop_wandering() + body.start_automove(pick(orange(20, body))) + addtimer(CALLBACK(body, TYPE_PROC_REF(/mob/living/simple_animal/hostile/giant_spider, disable_stop_automated_movement)), 5 SECONDS) diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_guard.dm b/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_guard.dm new file mode 100644 index 00000000000..81e9edbc1dd --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_guard.dm @@ -0,0 +1,66 @@ + +/datum/mob_controller/aggressive/giant_spider/guard + expected_type = /mob/living/simple_animal/hostile/giant_spider/guard + break_stuff_probability = 15 + var/vengance + var/berserking + var/weakref/paired_nurse + +/datum/mob_controller/aggressive/giant_spider/guard/do_process(time_elapsed) + . = ..() + if(berserking) + return + if(!paired_nurse) + find_nurse() + if(paired_nurse && get_activity() == AI_ACTIVITY_IDLE && get_stance() == STANCE_IDLE) + protect(paired_nurse) + +/datum/mob_controller/aggressive/giant_spider/guard/handle_death(gibbed) + . = ..() + if(paired_nurse) + var/datum/mob_controller/aggressive/giant_spider/nurse/paired_nurse_instance = paired_nurse.resolve() + if(istype(paired_nurse_instance) && paired_nurse_instance.paired_guard == weakref(src)) + paired_nurse_instance.paired_guard = null + paired_nurse = null + +/datum/mob_controller/aggressive/giant_spider/guard/proc/find_nurse() + for(var/mob/living/simple_animal/hostile/giant_spider/nurse/nurse in list_targets(10)) + if(nurse.stat || !istype(nurse.ai, /datum/mob_controller/aggressive/giant_spider/nurse)) + continue + var/datum/mob_controller/aggressive/giant_spider/nurse/nurse_ai = nurse.ai + if(nurse_ai.paired_guard) + continue + paired_nurse = weakref(nurse_ai) + nurse_ai.paired_guard = weakref(src) + return TRUE + return FALSE + +/datum/mob_controller/aggressive/giant_spider/guard/proc/protect(weakref/nurse) + stop_wandering() + var/datum/mob_controller/aggressive/giant_spider/nurse/paired_nurse_instance = paired_nurse?.resolve() + if(istype(paired_nurse_instance)) + var/static/datum/automove_metadata/_guard_nurse_metadata = new( + _acceptable_distance = 2 + ) + body.start_automove(paired_nurse_instance.body, metadata = _guard_nurse_metadata) + addtimer(CALLBACK(body, TYPE_PROC_REF(/mob/living/simple_animal/hostile/giant_spider, disable_stop_automated_movement)), 5 SECONDS) + +/datum/mob_controller/aggressive/giant_spider/guard/proc/go_berserk() + body.audible_message(SPAN_DANGER("\The [body] chitters wildly!")) + var/mob/living/simple_animal/critter = body + if(istype(critter)) + var/obj/item/attacking_with = critter.get_natural_weapon() + if(attacking_with) + attacking_with.force = initial(attacking_with.force) + 5 + break_stuff_probability = 45 + addtimer(CALLBACK(src, PROC_REF(calm_down)), 3 MINUTES) + +/datum/mob_controller/aggressive/giant_spider/guard/proc/calm_down() + berserking = FALSE + body.visible_message(SPAN_NOTICE("\The [body] calms down and surveys the area.")) + var/mob/living/simple_animal/critter = body + if(istype(critter)) + var/obj/item/attacking_with = critter.get_natural_weapon() + if(attacking_with) + attacking_with.force = initial(attacking_with.force) + break_stuff_probability = 10 diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_hunter.dm b/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_hunter.dm new file mode 100644 index 00000000000..9aa3d35e1c9 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_hunter.dm @@ -0,0 +1,9 @@ +/datum/mob_controller/aggressive/giant_spider/hunter + hunt_chance = 12 + break_stuff_probability = 30 + can_escape_buckles = TRUE + +/datum/mob_controller/aggressive/giant_spider/hunter/move_to_target(var/move_only = FALSE) + if(!body.can_act() || body.perform_maneuver(/decl/maneuver/leap/spider, get_target())) + return + ..() diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_nurse.dm b/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_nurse.dm new file mode 100644 index 00000000000..df383384daf --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spiders/ai_nurse.dm @@ -0,0 +1,138 @@ +/datum/mob_controller/aggressive/giant_spider/nurse + expected_type = /mob/living/simple_animal/hostile/giant_spider/nurse + break_stuff_probability = 10 + var/atom/cocoon_target + var/infest_chance = 8 + var/weakref/paired_guard + //things we can't encase in a cocoon + var/static/list/cocoon_blacklist = list( + /mob/living/simple_animal/hostile/giant_spider, + /obj/structure/closet + ) + +/datum/mob_controller/aggressive/giant_spider/nurse/do_process(time_elapsed) + . = ..() + + if(get_activity() != AI_ACTIVITY_IDLE || get_stance() != STANCE_IDLE) + return // We are doing something else, let it play out. + + var/mob/living/simple_animal/hostile/giant_spider/nurse/spooder = body + var/list/can_see = view(body, 10) + //30% chance to stop wandering and do something + if(prob(30)) + + // TODO: move webbing and coccoon creation into the mob attack procs so players can use them when possessing a spider nurse. + // first, check for potential food nearby to cocoon + for(var/mob/living/web_target in can_see) + if(is_type_in_list(web_target, cocoon_blacklist)) + continue + if(web_target.stat) + cocoon_target = web_target + set_activity(AI_ACTIVITY_MOVING_TO_TARGET) + body.start_automove(web_target) + //give up if we can't reach them after 10 seconds + give_up(web_target) + return + + //second, spin a sticky spiderweb on this tile + var/obj/effect/spider/stickyweb/W = locate() in get_turf(body) + if(!W) + set_activity(AI_ACTIVITY_BUILDING) + body.visible_message(SPAN_NOTICE("\The [body] begins to secrete a sticky substance.")) + pause() + spawn(4 SECONDS) + if(get_activity() == AI_ACTIVITY_BUILDING) + new /obj/effect/spider/stickyweb(body.loc) + set_activity(AI_ACTIVITY_IDLE) + resume() + else + //third, lay an egg cluster there + var/obj/effect/spider/eggcluster/E = locate() in get_turf(body) + if(!E && spooder.fed > 0 && spooder.max_eggs) + set_activity(AI_ACTIVITY_REPRODUCING) + body.visible_message(SPAN_NOTICE("\The [body] begins to lay a cluster of eggs.")) + pause() + spawn(5 SECONDS) + if(get_activity() == AI_ACTIVITY_REPRODUCING) + E = locate() in get_turf(body) + if(!E) + new /obj/effect/spider/eggcluster(body.loc, body) + spooder.max_eggs-- + spooder.fed-- + set_activity(AI_ACTIVITY_IDLE) + resume() + else + //fourthly, cocoon any nearby items so those pesky pinkskins can't use them + for(var/obj/O in can_see) + + if(O.anchored) + continue + + if(is_type_in_list(O, cocoon_blacklist)) + continue + + if(istype(O, /obj/item) || istype(O, /obj/structure) || istype(O, /obj/machinery)) + cocoon_target = O + set_activity(AI_ACTIVITY_MOVING_TO_TARGET) + stop_wandering() + body.start_automove(O) + //give up if we can't reach them after 10 seconds + give_up(O) + + else if(cocoon_target && body.Adjacent(cocoon_target) && get_activity() == AI_ACTIVITY_IDLE) + set_activity(AI_ACTIVITY_BUILDING) + body.visible_message(SPAN_NOTICE("\The [body] begins to secrete a sticky substance around \the [cocoon_target].")) + stop_wandering() + body.stop_automove() + spawn(5 SECONDS) + if(get_activity() == AI_ACTIVITY_BUILDING) + if(cocoon_target && isturf(cocoon_target.loc) && get_dist(body, cocoon_target) <= 1) + var/obj/effect/spider/cocoon/C = new(cocoon_target.loc) + var/large_cocoon = 0 + C.pixel_x = cocoon_target.pixel_x + C.pixel_y = cocoon_target.pixel_y + for(var/mob/living/M in C.loc) + large_cocoon = 1 + spooder.fed++ + spooder.max_eggs++ + body.visible_message(SPAN_WARNING("\The [body] sticks a proboscis into \the [cocoon_target] and sucks a viscous substance out.")) + M.forceMove(C) + C.pixel_x = M.pixel_x + C.pixel_y = M.pixel_y + break + for(var/obj/item/I in C.loc) + I.forceMove(C) + for(var/obj/structure/S in C.loc) + if(!S.anchored) + S.forceMove(C) + for(var/obj/machinery/M in C.loc) + if(!M.anchored) + M.forceMove(C) + if(large_cocoon) + C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") + cocoon_target = null + set_activity(AI_ACTIVITY_IDLE) + resume_wandering() + +/datum/mob_controller/aggressive/giant_spider/nurse/handle_death(gibbed) + . = ..() + if(!paired_guard) + return + var/datum/mob_controller/aggressive/giant_spider/guard/paired_guard_instance = paired_guard?.resolve() + if(istype(paired_guard_instance) && paired_guard_instance.paired_nurse == weakref(src)) + paired_guard_instance.vengance = rand(50,100) + if(prob(paired_guard_instance.vengance)) + paired_guard_instance.berserking = TRUE + paired_guard_instance.go_berserk() + paired_guard_instance.paired_nurse = null + paired_guard = null + +/datum/mob_controller/aggressive/giant_spider/nurse/proc/give_up(var/old_target) + set waitfor = FALSE + sleep(10 SECONDS) + if(get_activity() != AI_ACTIVITY_MOVING_TO_TARGET) + return + if(cocoon_target == old_target && !body.Adjacent(cocoon_target)) + cocoon_target = null + set_activity(AI_ACTIVITY_IDLE) + resume_wandering() diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spiders/guard.dm b/code/modules/mob/living/simple_animal/hostile/giant_spiders/guard.dm new file mode 100644 index 00000000000..d72f6b04828 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spiders/guard.dm @@ -0,0 +1,11 @@ +/mob/living/simple_animal/hostile/giant_spider/guard + desc = "A monstrously huge brown spider with shimmering eyes." + butchery_data = /decl/butchery_data/animal/arthropod/giant_spider/guard + max_health = 200 + natural_weapon = /obj/item/natural_weapon/bite/strong + poison_per_bite = 5 + base_movement_delay = 2 + ai = /datum/mob_controller/aggressive/giant_spider/guard + +/mob/living/simple_animal/hostile/giant_spider/guard/get_door_pry_time() + return 6 SECONDS diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spiders/hunter.dm b/code/modules/mob/living/simple_animal/hostile/giant_spiders/hunter.dm new file mode 100644 index 00000000000..1e941832bd9 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spiders/hunter.dm @@ -0,0 +1,39 @@ +/mob/living/simple_animal/hostile/giant_spider/hunter + desc = "A monstrously huge black spider with shimmering eyes." + icon = 'icons/mob/simple_animal/spider_black.dmi' + max_health = 150 + natural_weapon = /obj/item/natural_weapon/bite/strong + poison_per_bite = 10 + base_movement_delay = -1 + flash_protection = FLASH_PROTECTION_REDUCED + does_spin = FALSE + available_maneuvers = list(/decl/maneuver/leap/spider) + ability_cooldown = 3 MINUTES + ai = /datum/mob_controller/aggressive/giant_spider/hunter + var/leap_range = 5 + +/mob/living/simple_animal/hostile/giant_spider/hunter/get_door_pry_time() + return 5 SECONDS + +/mob/living/simple_animal/hostile/giant_spider/hunter/get_jump_distance() + return leap_range + +/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 + stop_automove() + if(istype(ai)) + ai.pause() + . = ..() + if(istype(ai)) + ai.resume() + +/mob/living/simple_animal/hostile/giant_spider/hunter/throw_impact(atom/hit_atom) + ..() + if(isliving(hit_atom)) + var/mob/living/target = hit_atom + if(istype(ai)) + ai.resume() + visible_message(SPAN_DANGER("\The [src] slams into \the [target], knocking them over!")) + SET_STATUS_MAX(target, STAT_WEAK, 1) + ai.move_to_target() diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spiders/nurse.dm b/code/modules/mob/living/simple_animal/hostile/giant_spiders/nurse.dm new file mode 100644 index 00000000000..2cc5dd4b274 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spiders/nurse.dm @@ -0,0 +1,33 @@ +/mob/living/simple_animal/hostile/giant_spider/nurse + desc = "A monstrously huge beige spider with shimmering eyes." + icon = 'icons/mob/simple_animal/spider_beige.dmi' + max_health = 80 + harm_intent_damage = 6 //soft + poison_per_bite = 5 + base_movement_delay = 0 + poison_type = /decl/material/liquid/sedatives + ai = /datum/mob_controller/aggressive/giant_spider/nurse + var/fed = 0 + var/max_eggs = 8 + +/mob/living/simple_animal/hostile/giant_spider/nurse/get_door_pry_time() + return 9 SECONDS + +/mob/living/simple_animal/hostile/giant_spider/nurse/Destroy() + . = ..() + divorce() + +/mob/living/simple_animal/hostile/giant_spider/nurse/apply_attack_effects(mob/living/target) + . = ..() + if(!ishuman(target) || max_eggs <= 0) + return + var/datum/mob_controller/aggressive/giant_spider/nurse/nurse_ai = ai + if(istype(nurse_ai) && !prob(nurse_ai.infest_chance)) + return + var/mob/living/human/H = target + var/list/limbs = H.get_external_organs() + var/obj/item/organ/external/O = LAZYLEN(limbs)? pick(limbs) : null + if(O && !BP_IS_PROSTHETIC(O) && !BP_IS_CRYSTAL(O) && (LAZYLEN(O.implants) < 2)) + var/eggs = new /obj/effect/spider/eggcluster(O, src) + LAZYADD(O.implants, eggs) + max_eggs-- diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spiders/spitter.dm b/code/modules/mob/living/simple_animal/hostile/giant_spiders/spitter.dm new file mode 100644 index 00000000000..1f2bb065e8e --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/giant_spiders/spitter.dm @@ -0,0 +1,30 @@ +//spitters - fast, comparatively weak, very venomous; projectile attacks but will resort to melee once out of ammo +/mob/living/simple_animal/hostile/giant_spider/spitter + desc = "A monstrously huge iridescent spider with shimmering eyes." + icon = 'icons/mob/simple_animal/spider_purple.dmi' + max_health = 90 + poison_per_bite = 15 + projectiletype = /obj/item/projectile/venom + projectilesound = 'sound/effects/hypospray.ogg' + fire_desc = "spits venom" + ranged_range = 6 + flash_protection = FLASH_PROTECTION_REDUCED + var/venom_charge = 16 + +/mob/living/simple_animal/hostile/giant_spider/spitter/get_door_pry_time() + return 7 SECONDS + +/mob/living/simple_animal/hostile/giant_spider/spitter/has_ranged_attack() + return venom_charge > 0 + +/mob/living/simple_animal/hostile/giant_spider/spitter/handle_regular_status_updates() + . = ..() + if(!.) + return FALSE + if(venom_charge <= 0 && prob(25)) + venom_charge++ + +/mob/living/simple_animal/hostile/giant_spider/spitter/shoot_at() + . = ..() + if(.) + venom_charge-- diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/_hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/_hivebot.dm new file mode 100644 index 00000000000..d958928874e --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/_hivebot.dm @@ -0,0 +1,32 @@ +/mob/living/simple_animal/hostile/hivebot + name = "hivebot" + desc = "A junky looking robot with four spiky legs." + icon = 'icons/mob/simple_animal/hivebot.dmi' + max_health = 55 + natural_weapon = /obj/item/natural_weapon/drone_slicer + projectilesound = 'sound/weapons/gunshot/gunshot_pistol.ogg' + projectiletype = /obj/item/projectile/beam/smalllaser + faction = "hivebot" + min_gas = null + max_gas = null + minbodytemp = 0 + natural_armor = list( + ARMOR_MELEE = ARMOR_MELEE_KNIVES + ) + bleed_colour = SYNTH_BLOOD_COLOR + gene_damage = -1 + base_animal_type = /mob/living/simple_animal/hostile/hivebot + butchery_data = /decl/butchery_data/synthetic + +/mob/living/simple_animal/hostile/hivebot/check_has_mouth() + return FALSE + +/mob/living/simple_animal/hostile/hivebot/get_death_message(gibbed) + return "blows apart!" + +/mob/living/simple_animal/hostile/hivebot/death(gibbed) + . = ..() + if(. && !gibbed) + new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc) + spark_at(src, cardinal_only = TRUE) + qdel(src) diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm similarity index 57% rename from code/modules/mob/living/simple_animal/hostile/hivebot.dm rename to code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm index f10abe8a1c4..707f9b5e749 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm @@ -1,77 +1,3 @@ -/mob/living/simple_animal/hostile/hivebot - name = "hivebot" - desc = "A junky looking robot with four spiky legs." - icon = 'icons/mob/simple_animal/hivebot.dmi' - max_health = 55 - natural_weapon = /obj/item/natural_weapon/drone_slicer - projectilesound = 'sound/weapons/gunshot/gunshot_pistol.ogg' - projectiletype = /obj/item/projectile/beam/smalllaser - faction = "hivebot" - min_gas = null - max_gas = null - minbodytemp = 0 - natural_armor = list( - ARMOR_MELEE = ARMOR_MELEE_KNIVES - ) - bleed_colour = SYNTH_BLOOD_COLOR - gene_damage = -1 - base_animal_type = /mob/living/simple_animal/hostile/hivebot - butchery_data = /decl/butchery_data/synthetic - -/mob/living/simple_animal/hostile/hivebot/check_has_mouth() - return FALSE - -/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 - base_movement_delay = 7 - -/mob/living/simple_animal/hostile/hivebot/rapid - ranged = 1 - rapid = 1 - -/mob/living/simple_animal/hostile/hivebot/strong - desc = "A junky looking robot with four spiky legs - this one has thick armour plating." - max_health = 120 - ranged = 1 - can_escape = 1 - natural_armor = list( - ARMOR_MELEE = ARMOR_MELEE_RESISTANT - ) - -/mob/living/simple_animal/hostile/hivebot/get_death_message(gibbed) - return "blows apart!" - -/mob/living/simple_animal/hostile/hivebot/death(gibbed) - . = ..() - if(. && !gibbed) - new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc) - spark_at(src, cardinal_only = TRUE) - qdel(src) - -/* -Special projectiles -*/ -/obj/item/projectile/bullet/gyro/megabot - name = "microrocket" - distance_falloff = 1.3 - fire_sound = 'sound/effects/Explosion1.ogg' - var/gyro_devastation = -1 - var/gyro_heavy_impact = 0 - var/gyro_light_impact = 1 - -/obj/item/projectile/bullet/gyro/megabot/on_hit(var/atom/target, var/blocked = 0) - if(isturf(target)) - explosion(target, gyro_devastation, gyro_heavy_impact, gyro_light_impact) - ..() - -/obj/item/projectile/beam/megabot - damage = 45 - distance_falloff = 0.5 - -/* -The megabot -*/ #define ATTACK_MODE_MELEE "melee" #define ATTACK_MODE_LASER "laser" #define ATTACK_MODE_ROCKET "rocket" @@ -86,18 +12,32 @@ The megabot ARMOR_MELEE = ARMOR_MELEE_RESISTANT, ARMOR_BULLET = ARMOR_BALLISTIC_PISTOL ) - can_escape = TRUE armor_type = /datum/extension/armor/toggle ability_cooldown = 3 MINUTES - pixel_x = -32 default_pixel_x = -32 + ai = /datum/mob_controller/aggressive/megahivebot base_movement_delay = 0 var/attack_mode = ATTACK_MODE_MELEE var/num_shots var/deactivated +/datum/mob_controller/aggressive/megahivebot + can_escape_buckles = TRUE + +/datum/mob_controller/aggressive/megahivebot/open_fire() + var/mob/living/simple_animal/hostile/hivebot/mega/megabot = body + if(!istype(megabot)) + return ..() + if(megabot.num_shots <= 0) + if(megabot.attack_mode == ATTACK_MODE_ROCKET) + megabot.switch_mode(ATTACK_MODE_LASER) + else + megabot.switch_mode(ATTACK_MODE_MELEE) + return + return ..() + /obj/item/natural_weapon/circular_saw name = "giant circular saw" attack_verb = list("sawed", "ripped") @@ -134,6 +74,9 @@ The megabot if(ATTACK_MODE_ROCKET) add_overlay("[icon_state]-rocket") +/mob/living/simple_animal/hostile/hivebot/mega/has_ranged_attack() + return attack_mode != ATTACK_MODE_MELEE && num_shots > 0 + /mob/living/simple_animal/hostile/hivebot/mega/proc/switch_mode(var/new_mode) if(!new_mode || new_mode == attack_mode) return @@ -141,7 +84,6 @@ The megabot switch(new_mode) if(ATTACK_MODE_MELEE) attack_mode = ATTACK_MODE_MELEE - ranged = FALSE projectilesound = null projectiletype = null num_shots = 0 @@ -149,7 +91,6 @@ The megabot deactivate() if(ATTACK_MODE_LASER) attack_mode = ATTACK_MODE_LASER - ranged = TRUE projectilesound = 'sound/weapons/Laser.ogg' projectiletype = /obj/item/projectile/beam/megabot num_shots = 12 @@ -157,9 +98,8 @@ The megabot visible_message(SPAN_MFAUNA("\The [src]'s laser cannon whines!")) if(ATTACK_MODE_ROCKET) attack_mode = ATTACK_MODE_ROCKET - ranged = TRUE projectilesound = 'sound/effects/Explosion1.ogg' - projectiletype = /obj/item/projectile/bullet/gyro/megabot + projectiletype = /obj/item/projectile/bullet/gyro/microrocket num_shots = 4 set_special_ability_cooldown(ability_cooldown) fire_desc = "launches a microrocket" @@ -168,7 +108,7 @@ The megabot update_icon() /mob/living/simple_animal/hostile/hivebot/mega/proc/deactivate() - stop_automation = TRUE + ai?.pause() deactivated = TRUE visible_message(SPAN_MFAUNA("\The [src] clicks loudly as its lights fade and its motors grind to a halt!")) update_icon() @@ -178,7 +118,7 @@ The megabot addtimer(CALLBACK(src, PROC_REF(reactivate)), 4 SECONDS) /mob/living/simple_animal/hostile/hivebot/mega/proc/reactivate() - stop_automation = FALSE + ai?.resume() deactivated = FALSE visible_message(SPAN_MFAUNA("\The [src] whirs back to life!")) var/datum/extension/armor/toggle/armor = get_extension(src, /datum/extension/armor) @@ -186,20 +126,11 @@ The megabot armor.toggle(TRUE) update_icon() -/mob/living/simple_animal/hostile/hivebot/mega/OpenFire(target_mob) - if(num_shots <= 0) - if(attack_mode == ATTACK_MODE_ROCKET) - switch_mode(ATTACK_MODE_LASER) - else - switch_mode(ATTACK_MODE_MELEE) - return - return ..() - -/mob/living/simple_animal/hostile/hivebot/mega/Shoot(target, start, user, bullet) +/mob/living/simple_animal/hostile/hivebot/mega/shoot_at(target, start, user, bullet) . = ..() if(.) num_shots-- #undef ATTACK_MODE_MELEE #undef ATTACK_MODE_LASER -#undef ATTACK_MODE_ROCKET \ No newline at end of file +#undef ATTACK_MODE_ROCKET diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/range.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/range.dm new file mode 100644 index 00000000000..dc9a29c6599 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/range.dm @@ -0,0 +1,6 @@ +/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." + base_movement_delay = 7 + +/mob/living/simple_animal/hostile/hivebot/range/has_ranged_attack() + return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/rapid.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/rapid.dm new file mode 100644 index 00000000000..6cdc6ffbd32 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/rapid.dm @@ -0,0 +1,5 @@ +/mob/living/simple_animal/hostile/hivebot/rapid + burst_projectile = TRUE + +/mob/living/simple_animal/hostile/hivebot/rapid/has_ranged_attack() + return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/strong.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/strong.dm new file mode 100644 index 00000000000..8030d50a9cb --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/strong.dm @@ -0,0 +1,13 @@ +/mob/living/simple_animal/hostile/hivebot/strong + desc = "A junky looking robot with four spiky legs - this one has thick armour plating." + max_health = 120 + natural_armor = list( + ARMOR_MELEE = ARMOR_MELEE_RESISTANT + ) + ai = /datum/mob_controller/hostile/hivebot_strong + +/datum/mob_controller/hostile/hivebot_strong + can_escape_buckles = TRUE + +/mob/living/simple_animal/hostile/hivebot/strong/has_ranged_attack() + return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm deleted file mode 100644 index 5b834046448..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ /dev/null @@ -1,284 +0,0 @@ -/mob/living/simple_animal/hostile - faction = "hostile" - 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 - var/attack_same = 0 - var/ranged = 0 - var/rapid = 0 - var/projectiletype - var/projectilesound - var/casingtype - var/fire_desc = "fires" //"X fire_desc at Y!" - var/ranged_range = 6 //tiles of range for ranged attackers to attack - - var/list/friends = list() - var/break_stuff_probability = 10 - var/destroy_surroundings = 1 - - var/stop_automation = FALSE //stops AI procs from running - - var/can_pry = TRUE - var/pry_time = 7 SECONDS //time it takes for mob to pry open a door - var/pry_desc = "prying" //"X begins pry_desc the door!" - - //hostile mobs will bash through these in order with their natural weapon - var/list/valid_obstacles_by_priority = list(/obj/structure/window, - /obj/structure/closet, - /obj/machinery/door/window, - /obj/structure/table, - /obj/structure/grille, - /obj/structure/barricade, - /obj/structure/wall_frame, - /obj/structure/railing) - -/mob/living/simple_animal/hostile/Destroy() - LAZYCLEARLIST(friends) - target_mob = null - return ..() - -/mob/living/simple_animal/hostile/can_act() - return !stop_automation && ..() - -/mob/living/simple_animal/hostile/proc/kick_stance() - if(target_mob) - stance = HOSTILE_STANCE_ATTACK - else - stance = HOSTILE_STANCE_IDLE - -/mob/living/simple_animal/hostile/proc/FindTarget() - if(!can_act()) - return null - if(!faction) //No faction, no reason to attack anybody. - return null - stop_wandering = FALSE - for(var/atom/A in ListTargets(10)) - var/atom/F = Found(A) - if(F) - face_atom(F) - return F - - if(ValidTarget(A)) - stance = HOSTILE_STANCE_ATTACK - face_atom(A) - return A - -/mob/living/simple_animal/hostile/proc/ValidTarget(var/atom/A) - if(A == src) - return FALSE - - if(ismob(A)) - var/mob/M = A - if(M.faction == src.faction && !attack_same) - return FALSE - else if(weakref(M) in friends) - return FALSE - if(M.stat) - return FALSE - - if(ishuman(M)) - var/mob/living/human/H = M - if (H.is_cloaked()) - return FALSE - - return TRUE - -/mob/living/simple_animal/hostile/proc/Found(var/atom/A) - return - -/mob/living/simple_animal/proc/MoveToTarget(var/move_only = FALSE) - return - -/mob/living/simple_animal/hostile/MoveToTarget(var/move_only = FALSE) - if(!can_act()) - return - if(HAS_STATUS(src, STAT_CONFUSE)) - set_moving_slowly() - start_automove(pick(orange(2, src))) - return - stop_wandering = TRUE - if(QDELETED(target_mob) || SA_attackable(target_mob)) - stance = HOSTILE_STANCE_IDLE - if(target_mob in ListTargets(10)) - if(ranged) - if(get_dist(src, target_mob) <= ranged_range) - if(!move_only) - OpenFire(target_mob) - else - set_moving_quickly() - start_automove(target_mob) - else - stance = HOSTILE_STANCE_ATTACKING - set_moving_quickly() - start_automove(target_mob) - -/mob/living/simple_animal/hostile/proc/handle_attacking_target() - stop_wandering = TRUE - if(!target_mob || SA_attackable(target_mob)) - LoseTarget() - return 0 - if(!(target_mob in ListTargets(10))) - LostTarget() - return 0 - if (ishuman(target_mob)) - var/mob/living/human/H = target_mob - if (H.is_cloaked()) - LoseTarget() - return 0 - if(next_move >= world.time) - return 0 - if(get_dist(src, target_mob) <= 1) //Attacking - attack_target(target_mob) - return 1 - -/mob/living/simple_animal/hostile/proc/LoseTarget() - stance = HOSTILE_STANCE_IDLE - target_mob = null - stop_automove() - -/mob/living/simple_animal/hostile/proc/LostTarget() - stance = HOSTILE_STANCE_IDLE - stop_automove() - -/mob/living/simple_animal/hostile/proc/ListTargets(var/dist = 7) - return hearers(src, dist)-src - -/mob/living/simple_animal/hostile/handle_regular_status_updates() - . = ..() - if(!.) - stop_automove() - -/mob/living/simple_animal/hostile/do_delayed_life_action() - ..() - if(!can_act()) - stop_automove() - kick_stance() - return 0 - - if(isturf(src.loc) && !src.buckled) - switch(stance) - if(HOSTILE_STANCE_IDLE) - target_mob = FindTarget() - - if(HOSTILE_STANCE_ATTACK) - face_atom(target_mob) - if(destroy_surroundings) - DestroySurroundings() - MoveToTarget() - - if(HOSTILE_STANCE_ATTACKING) - face_atom(target_mob) - if(destroy_surroundings) - DestroySurroundings() - handle_attacking_target() - if(HOSTILE_STANCE_INSIDE) //we aren't inside something so just switch - stance = HOSTILE_STANCE_IDLE - else - if(stance != HOSTILE_STANCE_INSIDE) - stance = HOSTILE_STANCE_INSIDE - stop_automove() - target_mob = null - -/mob/living/simple_animal/hostile/attackby(var/obj/item/O, var/mob/user) - var/oldhealth = current_health - . = ..() - if(current_health < oldhealth && !incapacitated(INCAPACITATION_KNOCKOUT)) - target_mob = user - MoveToTarget(move_only = TRUE) - -/mob/living/simple_animal/hostile/default_hurt_interaction(mob/user) - . = ..() - if(. && !incapacitated(INCAPACITATION_KNOCKOUT)) - target_mob = user - MoveToTarget(move_only = TRUE) - -/mob/living/simple_animal/hostile/bullet_act(var/obj/item/projectile/Proj) - var/oldhealth = current_health - . = ..() - if(isliving(Proj.firer) && !target_mob && current_health < oldhealth && !incapacitated(INCAPACITATION_KNOCKOUT)) - target_mob = Proj.firer - MoveToTarget(move_only = TRUE) - -/mob/living/simple_animal/hostile/proc/OpenFire(target_mob) - - if(!can_act()) - return FALSE - - var/target = target_mob - visible_message(SPAN_DANGER("\The [src] [fire_desc] at \the [target]!")) - - if(rapid) - var/datum/callback/shoot_cb = CALLBACK(src, PROC_REF(shoot_wrapper), target, loc, src) - addtimer(shoot_cb, 1) - addtimer(shoot_cb, 4) - addtimer(shoot_cb, 6) - else if(Shoot(target, src.loc, src) && casingtype) - new casingtype(get_turf(src)) - - stance = HOSTILE_STANCE_IDLE - target_mob = null - return TRUE - -/mob/living/simple_animal/hostile/proc/shoot_wrapper(target, location, user) - if(Shoot(target, location, user) && casingtype) - new casingtype(loc) - -/mob/living/simple_animal/hostile/proc/Shoot(var/target, var/start, var/user, var/bullet = 0) - if(!can_act() || target == start) - return FALSE - var/obj/item/projectile/A = new projectiletype(get_turf(user)) - if(!A) - return FALSE - playsound(user, projectilesound, 100, 1) - A.launch(target, get_exposed_defense_zone(target)) - return TRUE - -/mob/living/simple_animal/hostile/proc/DestroySurroundings() //courtesy of Lohikar - if(!can_act() || !target_mob) - return - if(prob(break_stuff_probability) && !Adjacent(target_mob)) - face_atom(target_mob) - var/turf/targ = get_step_resolving_mimic(get_turf(src), get_dir(src, target_mob)) - if(!targ) - return - - var/obj/effect/shield/S = locate(/obj/effect/shield) in targ - if(S && S.gen && S.gen.check_flag(MODEFLAG_NONHUMANS)) - UnarmedAttack(S) - return - - for(var/type in valid_obstacles_by_priority) - var/obj/obstacle = locate(type) in targ - if(obstacle) - UnarmedAttack(obstacle) - return - - if(can_pry) - for(var/obj/machinery/door/obstacle in targ) - if(obstacle.density) - if(!obstacle.can_open(1)) - return - face_atom(obstacle) - var/pry_time_holder = (obstacle.pry_mod * pry_time) - pry_door(src, pry_time_holder, obstacle) - return - -/mob/living/simple_animal/hostile/proc/pry_door(var/mob/user, var/delay, var/obj/machinery/door/pesky_door) - visible_message("\The [user] begins [pry_desc] at \the [pesky_door]!") - stop_automation = TRUE - if(do_after(user, delay, pesky_door)) - pesky_door.open(1) - stop_automation = FALSE - else - visible_message("\The [user] is interrupted.") - stop_automation = FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/leech.dm b/code/modules/mob/living/simple_animal/hostile/leech.dm index f7c7cecb1eb..5e46366bff2 100644 --- a/code/modules/mob/living/simple_animal/hostile/leech.dm +++ b/code/modules/mob/living/simple_animal/hostile/leech.dm @@ -7,14 +7,19 @@ natural_weapon = /obj/item/natural_weapon/bite/weak pass_flags = PASS_FLAG_TABLE faction = "leeches" - can_pry = FALSE - break_stuff_probability = 5 flash_protection = FLASH_PROTECTION_MAJOR bleed_colour = COLOR_VIOLET + ai = /datum/mob_controller/aggressive/leech var/suck_potency = 8 var/belly = 100 +/datum/mob_controller/aggressive/leech + break_stuff_probability = 5 + +/mob/living/simple_animal/hostile/can_pry_door() + return FALSE + /mob/living/simple_animal/hostile/leech/exoplanet/Initialize() adapt_to_current_level() . = ..() @@ -22,15 +27,15 @@ /mob/living/simple_animal/hostile/leech/handle_regular_status_updates() . = ..() if(.) - if(target_mob) + if(istype(ai) && ai.get_target()) belly -= 3 else belly -= 1 -/mob/living/simple_animal/hostile/leech/attack_target(mob/target) +/mob/living/simple_animal/hostile/leech/apply_attack_effects(mob/living/target) . = ..() - if(ishuman(.) && belly <= 75) - var/mob/living/human/H = . + if(ishuman(target) && belly <= 75) + var/mob/living/human/H = target var/obj/item/clothing/suit/space/S = H.get_covering_equipped_item_by_zone(BP_CHEST) if(istype(S) && !length(S.breaches)) return diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm index 54f2486779d..52a92826a17 100644 --- a/code/modules/mob/living/simple_animal/hostile/mimic.dm +++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm @@ -31,16 +31,34 @@ var/global/list/protected_objects = list( min_gas = null max_gas = null minbodytemp = 0 + pass_flags = PASS_FLAG_TABLE faction = "mimic" + ai = /datum/mob_controller/aggressive/mimic 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 var/knockdown_people = 0 - pass_flags = PASS_FLAG_TABLE + var/awake = TRUE + +// Return a list of targets that isn't the creator +/datum/mob_controller/aggressive/mimic/list_targets(var/dist = 7) + var/mob/living/simple_animal/hostile/mimic/mimic = body + . = istype(mimic) && mimic.awake && ..() + if(length(.) && mimic.creator) + . -= mimic.creator.resolve() + +/datum/mob_controller/aggressive/mimic/destroy_surroundings() + var/mob/living/simple_animal/hostile/mimic/mimic = body + . = istype(mimic) && mimic.awake && ..() + +/datum/mob_controller/aggressive/mimic/find_target() + . = ..() + if(.) + body.custom_emote(AUDIBLE_MESSAGE, "growls at [.]") /mob/living/simple_animal/hostile/mimic/on_update_icon() SHOULD_CALL_PARENT(FALSE) @@ -57,16 +75,8 @@ var/global/list/protected_objects = list( o = new o(loc) CopyObject(o,creator) -/mob/living/simple_animal/hostile/mimic/FindTarget() - . = ..() - if(.) - audible_emote("growls at [.]") - -/mob/living/simple_animal/hostile/mimic/ListTargets(var/dist = 7) - // Return a list of targets that isn't the creator - . = ..() - if(creator) - . -= creator.resolve() + if(!awake && istype(ai)) + ai.stop_wandering() /mob/living/simple_animal/hostile/mimic/proc/CopyObject(var/obj/O, var/mob/living/creator) @@ -77,11 +87,12 @@ var/global/list/protected_objects = list( var/obj/item/attacking_with = get_natural_weapon() if(istype(O, /obj/structure)) current_health = (anchored * 50) + 50 - destroy_objects = 1 + ai?.try_destroy_surroundings = TRUE if(O.density && O.anchored) knockdown_people = 1 attacking_with.force = 2 * initial(attacking_with.force) else if(istype(O, /obj/item)) + ai?.try_destroy_surroundings = FALSE var/obj/item/I = O current_health = 15 * I.w_class attacking_with.force = 2 + initial(I.force) @@ -138,46 +149,31 @@ var/global/list/protected_objects = list( M.dropInto(loc) qdel(src) -/mob/living/simple_animal/hostile/mimic/DestroySurroundings() - if(destroy_objects) - ..() - -/mob/living/simple_animal/hostile/mimic/attack_target(mob/target) - . =..() - if(knockdown_people) - var/mob/living/L = . - if(istype(L)) - if(prob(15)) - SET_STATUS_MAX(L, STAT_WEAK, 1) - L.visible_message("\the [src] knocks down \the [L]!") +/mob/living/simple_animal/hostile/mimic/apply_attack_effects(mob/living/target) + . = ..() + if(knockdown_people && prob(15)) + SET_STATUS_MAX(target, STAT_WEAK, 1) + target.visible_message(SPAN_DANGER("\The [src] knocks down \the [target]!")) /mob/living/simple_animal/hostile/mimic/Destroy() copy_of = null creator = null return ..() -/mob/living/simple_animal/hostile/mimic/sleeping - wander = FALSE - stop_wandering = TRUE - - var/awake = 0 - -/mob/living/simple_animal/hostile/mimic/sleeping/ListTargets(var/dist = 7) - . = awake && ..() - -/mob/living/simple_animal/hostile/mimic/sleeping/proc/trigger() +/mob/living/simple_animal/hostile/mimic/proc/trigger() if(!awake) src.visible_message("\The [src] starts to move!") - awake = 1 + awake = TRUE -/mob/living/simple_animal/hostile/mimic/sleeping/adjustBruteLoss(var/damage, var/do_update_health = FALSE) +/mob/living/simple_animal/hostile/mimic/adjustBruteLoss(var/damage, var/do_update_health = FALSE) ..(damage) - trigger() + if(!awake) + trigger() -/mob/living/simple_animal/hostile/mimic/sleeping/attack_hand() - trigger() +/mob/living/simple_animal/hostile/mimic/attack_hand() + if(!awake) + trigger() return ..() -/mob/living/simple_animal/hostile/mimic/sleeping/DestroySurroundings() - if(awake) - ..() \ No newline at end of file +/mob/living/simple_animal/hostile/mimic/sleeping + awake = FALSE \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/pike.dm b/code/modules/mob/living/simple_animal/hostile/pike.dm index b53847b7527..cea25ce1e0e 100644 --- a/code/modules/mob/living/simple_animal/hostile/pike.dm +++ b/code/modules/mob/living/simple_animal/hostile/pike.dm @@ -2,24 +2,25 @@ name = "space pike" desc = "A bigger, angrier cousin of the space carp." icon = 'icons/mob/simple_animal/spaceshark.dmi' - 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 mob_size = MOB_SIZE_LARGE offset_overhead_text_x = 16 pixel_x = -16 max_health = 150 - harm_intent_damage = 5 natural_weapon = /obj/item/natural_weapon/bite/pike - can_escape = TRUE + butchery_data = /decl/butchery_data/animal/fish/space_carp/pike + ai = /datum/mob_controller/aggressive/carp/pike +/datum/mob_controller/aggressive/carp/pike + turns_per_wander = 4 + attack_same_faction = TRUE break_stuff_probability = 55 - butchery_data = /decl/butchery_data/animal/fish/space_carp/pike + can_escape_buckles = TRUE /obj/item/natural_weapon/bite/pike force = 25 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 1d9fe7cac67..51b6f0523f4 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm @@ -1,16 +1,10 @@ -/mob/living/simple_animal/hostile/retaliate/clown +/mob/living/simple_animal/hostile/clown name = "clown" desc = "A denizen of clown planet" icon = 'icons/mob/simple_animal/clown.dmi' - 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_wandering_when_pulled = FALSE max_health = 75 harm_intent_damage = 8 - can_escape = TRUE minbodytemp = 270 maxbodytemp = 370 heat_damage_per_tick = 15 //amount of damage applied if animal's body temperature is higher than maxbodytemp @@ -18,7 +12,16 @@ unsuitable_atmos_damage = 10 natural_weapon = /obj/item/natural_weapon/clown faction = "circus" - base_movement_delay = -1 + ai = /datum/mob_controller/aggressive/clown + +/datum/mob_controller/aggressive/clown + turns_per_wander = 10 + emote_speech = list("HONK", "Honk!", "Welcome to clown planet!") + emote_see = list("honks") + speak_chance = 0.25 + stop_wander_when_pulled = FALSE + only_attack_enemies = TRUE + can_escape_buckles = TRUE /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 7f1994a5e10..aa7216517c9 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -1,17 +1,11 @@ //malfunctioning combat drones -/mob/living/simple_animal/hostile/retaliate/malf_drone +/mob/living/simple_animal/hostile/malf_drone name = "combat drone" desc = "An automated combat drone armed with state of the art weaponry and shielding." icon = 'icons/mob/simple_animal/drone_combat.dmi' - ranged = 1 - rapid = 0 - speak_chance = 2.5 - 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") + burst_projectile = 0 a_intent = I_HURT - stop_wandering_when_pulled = FALSE max_health = 300 move_intents = list( /decl/move_intent/walk/animal_slow, @@ -19,32 +13,23 @@ ) 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 - - //the drone randomly switches between these states if it's malfunctioning - var/malfunctioning = 1 - var/hostile_drone = 0 - //0 - retaliate, only attack enemies that attack it - //1 - hostile, attack everything that comes near - var/hostile_range = 10 - - var/explode_chance = 1 - var/disabled = 0 - var/exploding = 0 + ai = /datum/mob_controller/aggressive/malf_drone //Drones aren't affected by atmos. min_gas = null max_gas = null minbodytemp = 0 + faction = "malf_drone" var/has_loot = 1 - faction = "malf_drone" + var/datum/effect/effect/system/trail/ion_trail + var/explode_chance = 1 + var/disabled = 0 + var/exploding = 0 var/static/list/debris = list( /decl/material/solid/glass = /obj/item/shard, @@ -52,13 +37,37 @@ /decl/material/solid/metal/plasteel = null ) -/mob/living/simple_animal/hostile/retaliate/malf_drone/check_has_mouth() +/datum/mob_controller/aggressive/malf_drone + speak_chance = 1.25 + turns_per_wander = 6 + 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") + stop_wander_when_pulled = FALSE + only_attack_enemies = TRUE + try_destroy_surroundings = FALSE + //the drone randomly switches between these states if it's malfunctioning + var/malfunctioning = 1 + var/hostile_drone = 0 + //0 - retaliate, only attack enemies that attack it + //1 - hostile, attack everything that comes near + var/hostile_range = 10 + +/mob/living/simple_animal/hostile/malf_drone/has_ranged_attack() + return TRUE + +/datum/mob_controller/aggressive/malf_drone/list_targets(var/dist = 7) + . = ..(hostile_drone ? hostile_range : dist) + for(var/mob/M in .) + if(istype(M, body.type)) + . -= M + +/mob/living/simple_animal/hostile/malf_drone/check_has_mouth() return FALSE -/mob/living/simple_animal/hostile/retaliate/malf_drone/can_act() +/mob/living/simple_animal/hostile/malf_drone/can_act() return disabled <= 0 && ..() -/mob/living/simple_animal/hostile/retaliate/malf_drone/Initialize() +/mob/living/simple_animal/hostile/malf_drone/Initialize() . = ..() if(prob(5)) projectiletype = /obj/item/projectile/beam/pulse/drone @@ -67,26 +76,21 @@ ion_trail.set_up(src) ion_trail.start() -/mob/living/simple_animal/hostile/retaliate/malf_drone/Process_Spacemove() +/mob/living/simple_animal/hostile/malf_drone/Process_Spacemove() return 1 -/mob/living/simple_animal/hostile/retaliate/malf_drone/proc/Haywire() - if(prob(disabled ? 0 : 1) && malfunctioning) - if(hostile_drone) +/mob/living/simple_animal/hostile/malf_drone/proc/Haywire() + var/datum/mob_controller/aggressive/malf_drone/drone_ai = ai + if(prob(disabled ? 0 : 1) && istype(drone_ai) && drone_ai.malfunctioning) + if(drone_ai.hostile_drone) src.visible_message("[html_icon(src)] [src] retracts several targetting vanes, and dulls it's running lights.") - hostile_drone = 0 + drone_ai.hostile_drone = 0 else src.visible_message("[html_icon(src)] [src] suddenly lights up, and additional targetting vanes slide into place.") - hostile_drone = 1 - -/mob/living/simple_animal/hostile/retaliate/malf_drone/ListTargets(var/dist = 7) - . = ..(hostile_drone ? hostile_range : dist) - for(var/mob/M in .) - if(istype(M, type)) - . -= M + drone_ai.hostile_drone = 1 //self repair systems have a chance to bring the drone back to life -/mob/living/simple_animal/hostile/retaliate/malf_drone/handle_living_non_stasis_processes() +/mob/living/simple_animal/hostile/malf_drone/handle_living_non_stasis_processes() . = ..() if(!.) return FALSE @@ -95,12 +99,14 @@ if(disabled > 0) set_stat(UNCONSCIOUS) disabled-- - wander = FALSE - speak_chance = 0 + if(istype(ai)) + ai.stop_wandering() + ai.speak_chance = 0 if(disabled <= 0) set_stat(CONSCIOUS) - wander = TRUE - speak_chance = 2.5 + if(istype(ai)) + ai.resume_wandering() + ai.speak_chance = 1.25 //repair a bit of damage if(prob(1)) @@ -146,7 +152,7 @@ if(!exploding && !disabled && prob(explode_chance)) exploding = 1 set_stat(UNCONSCIOUS) - wander = TRUE + ai?.resume_wandering() stop_automove() spawn(rand(50,150)) if(!disabled && exploding) @@ -155,7 +161,7 @@ update_icon() -/mob/living/simple_animal/hostile/retaliate/malf_drone/on_update_icon() +/mob/living/simple_animal/hostile/malf_drone/on_update_icon() . = ..() if(stat != DEAD) var/current_max_health = get_max_health() @@ -167,28 +173,30 @@ icon_state = "[icon_state]-shield2" //ion rifle! -/mob/living/simple_animal/hostile/retaliate/malf_drone/emp_act(severity) +/mob/living/simple_animal/hostile/malf_drone/emp_act(severity) take_damage(rand(3,15) * (severity + 1), BURN) disabled = rand(150, 600) - hostile_drone = 0 + var/datum/mob_controller/aggressive/malf_drone/drone_brain = ai + if(istype(drone_brain)) + drone_brain.hostile_drone = 0 stop_automove() -/mob/living/simple_animal/hostile/retaliate/malf_drone/get_death_message(gibbed) +/mob/living/simple_animal/hostile/malf_drone/get_death_message(gibbed) return "suddenly breaks apart." -/mob/living/simple_animal/hostile/retaliate/malf_drone/get_self_death_message(gibbed) +/mob/living/simple_animal/hostile/malf_drone/get_self_death_message(gibbed) return "You have been destroyed." -/mob/living/simple_animal/hostile/retaliate/malf_drone/death(gibbed) +/mob/living/simple_animal/hostile/malf_drone/death(gibbed) . = ..() if(. && !gibbed) physically_destroyed() -/mob/living/simple_animal/hostile/retaliate/malf_drone/Destroy() +/mob/living/simple_animal/hostile/malf_drone/Destroy() QDEL_NULL(ion_trail) return ..() -/mob/living/simple_animal/hostile/retaliate/malf_drone/physically_destroyed(skip_qdel) +/mob/living/simple_animal/hostile/malf_drone/physically_destroyed(skip_qdel) //some random debris left behind if(has_loot) spark_at(src, cardinal_only = TRUE) 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 92b92b0f5e2..65627cdfdb1 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm @@ -1,57 +1,17 @@ - -/mob/living/simple_animal/hostile/retaliate/beast - ai = /datum/mob_controller/beast +/mob/living/simple_animal/hostile/beast + ai = /datum/mob_controller/aggressive/beast + abstract_type = /mob/living/simple_animal/hostile/beast nutrition = 300 - var/list/prey -/mob/living/simple_animal/hostile/retaliate/beast/get_satiated_nutrition() +/mob/living/simple_animal/hostile/beast/get_nutrition() + return nutrition + +/mob/living/simple_animal/hostile/beast/get_satiated_nutrition() return 250 -/mob/living/simple_animal/hostile/retaliate/beast/get_max_nutrition() +/mob/living/simple_animal/hostile/beast/get_max_nutrition() return 300 -/mob/living/simple_animal/hostile/retaliate/beast/ListTargets(var/dist = 7) - . = ..() - if(!length(.)) - if(LAZYLEN(prey)) - . = list() - for(var/weakref/W in prey) - var/mob/M = W.resolve() - if(M) - . |= M - else if(get_nutrition() < get_max_nutrition() * 0.75) //time to look for some food - for(var/mob/living/L in view(src, dist)) - if(!attack_same && L.faction != faction) - LAZYDISTINCTADD(prey, weakref(L)) - -/datum/mob_controller/beast - expected_type = /mob/living/simple_animal/hostile/retaliate/beast - -/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() - if(nut > max_nut * 0.75 || beast.incapacitated()) - LAZYCLEARLIST(beast.prey) - return - for(var/mob/living/simple_animal/S in range(beast,1)) - if(S == beast) - continue - if(S.stat != DEAD) - continue - beast.visible_message(SPAN_DANGER("\The [beast] consumes the body of \the [S]!")) - var/turf/T = get_turf(S) - var/remains_type = S.get_remains_type() - if(remains_type) - var/obj/item/remains/X = new remains_type(T) - X.desc += "These look like they belonged to \a [S.name]." - beast.adjust_nutrition(5 * S.get_max_health()) - if(prob(5)) - S.gib() - else - qdel(S) - break - /mob/living/simple_animal/proc/name_species() set name = "Name Alien Species" set category = "IC" @@ -73,7 +33,7 @@ to_chat(usr, SPAN_WARNING("This species has already been named!")) return -/mob/living/simple_animal/hostile/retaliate/beast/samak +/mob/living/simple_animal/hostile/beast/samak name = "samak" desc = "A fast, armoured predator accustomed to hiding and ambushing in cold terrain." faction = "samak" @@ -85,20 +45,23 @@ max_health = 125 natural_weapon = /obj/item/natural_weapon/claws cold_damage_per_tick = 0 - speak_chance = 2.5 - emote_speech = list("Hruuugh!","Hrunnph") - emote_see = list("paws the ground","shakes its mane","stomps") - emote_hear = list("snuffles") + ai = /datum/mob_controller/aggressive/beast/samak natural_armor = list( ARMOR_MELEE = ARMOR_MELEE_KNIVES ) base_movement_delay = 2 -/mob/living/simple_animal/hostile/retaliate/beast/samak/alt +/datum/mob_controller/aggressive/beast/samak + speak_chance = 1.25 + emote_speech = list("Hruuugh!","Hrunnph") + emote_see = list("paws the ground","shakes its mane","stomps") + emote_hear = list("snuffles") + +/mob/living/simple_animal/hostile/beast/samak/alt desc = "A fast, armoured predator accustomed to hiding and ambushing." icon = 'icons/mob/simple_animal/samak_alt.dmi' -/mob/living/simple_animal/hostile/retaliate/beast/diyaab +/mob/living/simple_animal/hostile/beast/diyaab name = "diyaab" desc = "A small pack animal. Although omnivorous, it will hunt meat on occasion." faction = "diyaab" @@ -110,14 +73,17 @@ max_health = 25 natural_weapon = /obj/item/natural_weapon/claws/weak cold_damage_per_tick = 0 - speak_chance = 2.5 + mob_size = MOB_SIZE_SMALL + ai = /datum/mob_controller/aggressive/beast/diyaab + base_movement_delay = 1 + +/datum/mob_controller/aggressive/beast/diyaab + speak_chance = 1.25 emote_speech = list("Awrr?","Aowrl!","Worrl") 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 +/mob/living/simple_animal/hostile/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" @@ -129,13 +95,19 @@ max_health = 75 natural_weapon = /obj/item/natural_weapon/claws cold_damage_per_tick = 0 - speak_chance = 1 + ai = /datum/mob_controller/aggressive/beast/shantak + +/datum/mob_controller/aggressive/beast/shantak + speak_chance = 0.5 emote_speech = list("Shuhn","Shrunnph?","Shunpf") emote_see = list("scratches the ground","shakes out its mane","tinkles gently") -/mob/living/simple_animal/hostile/retaliate/beast/shantak/alt +/mob/living/simple_animal/hostile/beast/shantak/alt desc = "A piglike creature with a long and graceful mane. Don't be fooled by its beauty." icon = 'icons/mob/simple_animal/shantak_alt.dmi' + ai = /datum/mob_controller/aggressive/beast/shantak/alt + +/datum/mob_controller/aggressive/beast/shantak/alt emote_see = list("scratches the ground","shakes out it's mane","rustles softly") /mob/living/simple_animal/yithian @@ -154,11 +126,14 @@ name = "taki" desc = "It looks like a bunch of legs." icon = 'icons/mob/simple_animal/bug.dmi' - speak_chance = 0.5 - emote_hear = list("scratches the ground","chitters") + ai = /datum/mob_controller/thinbug mob_size = MOB_SIZE_MINISCULE -/mob/living/simple_animal/hostile/retaliate/royalcrab +/datum/mob_controller/thinbug + speak_chance = 0.25 + emote_hear = list("scratches the ground","chitters") + +/mob/living/simple_animal/hostile/royalcrab name = "cragenoy" desc = "It looks like a crustacean with an exceedingly hard carapace. Watch the pinchers!" faction = "crab" @@ -169,14 +144,18 @@ ) max_health = 150 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") + ai = /datum/mob_controller/aggressive/thinbug natural_armor = list( ARMOR_MELEE = ARMOR_MELEE_RESISTANT - ) + ) base_movement_delay = 1 -/mob/living/simple_animal/hostile/retaliate/beast/charbaby +/datum/mob_controller/aggressive/thinbug + speak_chance = 0.25 + emote_see = list("skitters","oozes liquid from its mouth", "scratches at the ground", "clicks its claws") + only_attack_enemies = TRUE + +/mob/living/simple_animal/hostile/beast/charbaby name = "charbaby" desc = "A huge grubby creature." icon = 'icons/mob/simple_animal/char.dmi' @@ -198,20 +177,21 @@ force = 5 attack_verb = list("singed") -/mob/living/simple_animal/hostile/retaliate/beast/charbaby/default_hurt_interaction(mob/user) +/mob/living/simple_animal/hostile/beast/charbaby/default_hurt_interaction(mob/user) . = ..() if(. && ishuman(user)) reflect_unarmed_damage(user, BURN, "amorphous mass") -/mob/living/simple_animal/hostile/retaliate/beast/charbaby/attack_target(mob/target) +/mob/living/simple_animal/hostile/beast/charbaby/apply_attack_effects(mob/living/target) . = ..() - if(isliving(target_mob) && prob(25)) - var/mob/living/L = target_mob - if(prob(10)) - L.adjust_fire_stacks(1) - L.IgniteMob() + if(prob(10)) + target.adjust_fire_stacks(1) + target.IgniteMob() -/mob/living/simple_animal/hostile/retaliate/beast/shantak/lava +/mob/living/simple_animal/hostile/beast/shantak/lava desc = "A vaguely canine looking beast. It looks as though its fur is made of stone wool." icon = 'icons/mob/simple_animal/lavadog.dmi' + ai = /datum/mob_controller/aggressive/beast/shantak/lava + +/datum/mob_controller/aggressive/beast/shantak/lava emote_speech = list("Karuph","Karump") \ No newline at end of file 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 613be5056b9..6419760e57b 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 @@ -1,18 +1,11 @@ -/mob/living/simple_animal/hostile/retaliate/giant_crab +/mob/living/simple_animal/hostile/giant_crab name = "giant crab" desc = "A gigantic crustacean with a blue shell. Its left claw is nearly twice the size of its right." icon = 'icons/mob/simple_animal/bluecrab.dmi' mob_size = MOB_SIZE_LARGE speak_emote = list("clicks") - emote_hear = list("clicks") - emote_see = list("clacks") - speak_chance = 0.5 - turns_per_wander = 5 butchery_data = /decl/butchery_data/animal/arthropod/crab/giant - can_escape = TRUE //snip snip - break_stuff_probability = 15 faction = "crabs" - pry_time = 2 SECONDS max_health = 350 natural_weapon = /obj/item/natural_weapon/pincers/giant return_damage_min = 2 @@ -23,7 +16,7 @@ ARMOR_BULLET = ARMOR_BALLISTIC_PISTOL ) ability_cooldown = 2 MINUTES - ai = /datum/mob_controller/giant_crab + ai = /datum/mob_controller/aggressive/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,63 +24,74 @@ 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/mob_controller/giant_crab - expected_type = /mob/living/simple_animal/hostile/retaliate/giant_crab +/datum/mob_controller/aggressive/giant_crab + break_stuff_probability = 15 + emote_hear = list("clicks") + emote_see = list("clacks") + speak_chance = 0.25 + turns_per_wander = 10 + expected_type = /mob/living/simple_animal/hostile/giant_crab + only_attack_enemies = TRUE + can_escape_buckles = TRUE -/datum/mob_controller/giant_crab/do_process(time_elapsed) +/datum/mob_controller/aggressive/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)) + var/mob/living/simple_animal/hostile/giant_crab/crab = body + if(!istype(crab) || body.stat) + return + crab.process_grab() + if((body.current_health > body.get_max_health() / 1.5) && LAZYLEN(get_enemies()) && prob(5)) if(crab.victim) crab.release_grab() - crab.enemies = list() - crab.LoseTarget() - crab.visible_message(SPAN_NOTICE("\The [crab] lowers its pincer.")) + clear_enemies() + lose_target() + body.visible_message(SPAN_NOTICE("\The [body] lowers its pincer.")) /obj/item/natural_weapon/pincers/giant force = 15 attack_verb = list("snipped", "pinched", "crushed") -/mob/living/simple_animal/hostile/retaliate/giant_crab/Initialize() //embiggen +/mob/living/simple_animal/hostile/giant_crab/Initialize() //embiggen . = ..() set_scale(1.5) -/mob/living/simple_animal/hostile/retaliate/giant_crab/Destroy() +/mob/living/simple_animal/hostile/giant_crab/get_door_pry_time() + return 2 SECONDS + +/mob/living/simple_animal/hostile/giant_crab/Destroy() . = ..() victim = null -/mob/living/simple_animal/hostile/retaliate/giant_crab/default_hurt_interaction(mob/user) +/mob/living/simple_animal/hostile/giant_crab/default_hurt_interaction(mob/user) . = ..() if(. && ishuman(user)) reflect_unarmed_damage(user, BRUTE, "armoured carapace") -/mob/living/simple_animal/hostile/retaliate/giant_crab/do_delayed_life_action() - ..() - process_grab() - -/mob/living/simple_animal/hostile/retaliate/giant_crab/attack_target(mob/target) +/mob/living/simple_animal/hostile/giant_crab/apply_attack_effects(mob/living/target) . = ..() - if(ishuman(.)) - var/mob/living/human/H = . - if(victim == H) - if(!Adjacent(victim)) - release_grab() - else if(prob(continue_grab_prob)) - SET_STATUS_MAX(H, STAT_WEAK, 1) - SET_STATUS_MAX(H, STAT_STUN, 1) - grab_damage++ - visible_message(SPAN_MFAUNA("\The [src] tightens its grip on \the [victim]!")) - return + if(!ishuman(target)) + return + + var/mob/living/human/H = target + if(victim == H) + if(!Adjacent(victim)) + release_grab() + else if(prob(continue_grab_prob)) + SET_STATUS_MAX(H, STAT_WEAK, 1) + SET_STATUS_MAX(H, STAT_STUN, 1) + grab_damage++ + visible_message(SPAN_MFAUNA("\The [src] tightens its grip on \the [victim]!")) + return - if(!victim && can_act() && !is_on_special_ability_cooldown() && Adjacent(H)) - events_repository.register(/decl/observ/destroyed, victim, src, PROC_REF(release_grab)) - victim = H - SET_STATUS_MAX(H, STAT_WEAK, grab_duration) - SET_STATUS_MAX(H, STAT_STUN, grab_duration) - visible_message(SPAN_MFAUNA("\The [src] catches \the [victim] in its powerful pincer!")) - stop_automation = TRUE + if(!victim && can_act() && !is_on_special_ability_cooldown() && Adjacent(H)) + events_repository.register(/decl/observ/destroyed, victim, src, PROC_REF(release_grab)) + victim = H + SET_STATUS_MAX(H, STAT_WEAK, grab_duration) + SET_STATUS_MAX(H, STAT_STUN, grab_duration) + visible_message(SPAN_MFAUNA("\The [src] catches \the [victim] in its powerful pincer!")) + ai?.pause() -/mob/living/simple_animal/hostile/retaliate/giant_crab/proc/process_grab() +/mob/living/simple_animal/hostile/giant_crab/proc/process_grab() if(victim && !incapacitated()) if(victim.stat >= UNCONSCIOUS || !Adjacent(victim) || !victim.incapacitated()) release_grab() @@ -95,11 +99,11 @@ visible_message(SPAN_DANGER("\The [src] [pick(grab_desc)] \the [victim] in its pincer!")) victim.apply_damage(grab_damage, BRUTE, BP_CHEST, DAM_EDGE, used_weapon = "crab's pincer") -/mob/living/simple_animal/hostile/retaliate/giant_crab/proc/release_grab() +/mob/living/simple_animal/hostile/giant_crab/proc/release_grab() if(victim) visible_message(SPAN_NOTICE("\The [src] releases its grip on \the [victim]!")) events_repository.unregister(/decl/observ/destroyed, victim) victim = null set_special_ability_cooldown(ability_cooldown) - stop_automation = FALSE + ai?.resume() grab_damage = initial(grab_damage) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/giant_parrot/giant_parrot.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_parrot/giant_parrot.dm index ce7d80f9cec..2ae451ec636 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/giant_parrot/giant_parrot.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_parrot/giant_parrot.dm @@ -1,12 +1,10 @@ -/mob/living/simple_animal/hostile/retaliate/parrot/space +/mob/living/simple_animal/hostile/parrot/space name = "space parrot" desc = "It could be some all-knowing being that, for reasons we could never hope to understand, is assuming the shape and general mannerisms of a parrot - or just a rather large bird." gender = FEMALE max_health = 750 mob_size = MOB_SIZE_LARGE speak_emote = list("professes","speaks unto you","elaborates","proclaims") - emote_speech = null - emote_hear = list("sings a song to herself", "preens herself") natural_weapon = /obj/item/natural_weapon/giant min_gas = null max_gas = null @@ -14,20 +12,25 @@ universal_understand = TRUE see_invisible = SEE_INVISIBLE_NOLIGHTING see_in_dark = 7 - can_escape = TRUE relax_chance = 60 //a gentle beast impatience = 10 parrot_isize = ITEM_SIZE_LARGE simple_parrot = TRUE ability_cooldown = 2 MINUTES butchery_data = /decl/butchery_data/animal/bird/parrot/space + ai = /datum/mob_controller/aggressive/parrot/space var/get_subspecies_name = TRUE -/mob/living/simple_animal/hostile/retaliate/parrot/space/proc/get_parrot_species() +/datum/mob_controller/aggressive/parrot/space + emote_speech = null + emote_hear = list("sings a song to herself", "preens herself") + can_escape_buckles = TRUE + +/mob/living/simple_animal/hostile/parrot/space/proc/get_parrot_species() var/list/parrot_species = decls_repository.get_decls_of_type(/decl/parrot_subspecies) return LAZYLEN(parrot_species) ? parrot_species[pick(parrot_species)] : null -/mob/living/simple_animal/hostile/retaliate/parrot/space/Initialize() +/mob/living/simple_animal/hostile/parrot/space/Initialize() . = ..() var/decl/parrot_subspecies/ps = get_parrot_species() if(ps) @@ -38,10 +41,10 @@ set_scale(2) update_icon() -/mob/living/simple_animal/hostile/retaliate/parrot/space/attack_target(mob/target) +/mob/living/simple_animal/hostile/parrot/space/apply_attack_effects(mob/living/target) . = ..() - if(ishuman(.) && can_act() && !is_on_special_ability_cooldown() && Adjacent(.)) - var/mob/living/human/H = . + if(ishuman(target) && can_act() && !is_on_special_ability_cooldown() && Adjacent(.)) + var/mob/living/human/H = target if(prob(70)) SET_STATUS_MAX(H, STAT_WEAK, rand(2,3)) set_special_ability_cooldown(ability_cooldown / 1.5) @@ -55,22 +58,25 @@ H.try_unequip(HAT, get_turf(src)) //subtypes -/mob/living/simple_animal/hostile/retaliate/parrot/space/lesser +/mob/living/simple_animal/hostile/parrot/space/lesser name = "Avatar of the Howling Dark" get_subspecies_name = FALSE natural_weapon = /obj/item/natural_weapon/large max_health = 300 -/mob/living/simple_animal/hostile/retaliate/parrot/space/lesser/get_parrot_species() +/mob/living/simple_animal/hostile/parrot/space/lesser/get_parrot_species() return GET_DECL(/decl/parrot_subspecies/black) -/mob/living/simple_animal/hostile/retaliate/parrot/space/megafauna +/mob/living/simple_animal/hostile/parrot/space/megafauna name = "giant parrot" desc = "A huge parrot-like bird." get_subspecies_name = FALSE max_health = 350 speak_emote = list("squawks") - emote_hear = list("preens itself") + ai = /datum/mob_controller/aggressive/parrot/space/megafauna natural_weapon = /obj/item/natural_weapon/large relax_chance = 30 - impatience = 5 \ No newline at end of file + impatience = 5 + +/datum/mob_controller/aggressive/parrot/space/megafauna + emote_hear = list("preens itself") diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm index 9cfcf18141d..4928a083d62 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/goose.dm @@ -1,18 +1,14 @@ -/mob/living/simple_animal/hostile/retaliate/goose +/mob/living/simple_animal/hostile/goose name = "goose" desc = "A large waterfowl, known for its beauty and quick temper when provoked." icon = 'icons/mob/simple_animal/goose.dmi' speak_emote = list("honks") - emote_speech = list("Honk!") - emote_hear = list("honks","flaps its wings","clacks") - emote_see = list("flaps its wings", "scratches the ground") natural_weapon = /obj/item/natural_weapon/goosefeet max_health = 45 pass_flags = PASS_FLAG_TABLE faction = "geese" - pry_time = 8 SECONDS - break_stuff_probability = 5 butchery_data = /decl/butchery_data/animal/small/fowl/goose + ai = /datum/mob_controller/aggressive/goose var/enrage_potency = 3 var/enrage_potency_loose = 4 @@ -20,6 +16,19 @@ var/max_damage = 25 var/loose = FALSE //goose loose status +/datum/mob_controller/aggressive/goose + break_stuff_probability = 5 + emote_speech = list("Honk!") + emote_hear = list("honks","flaps its wings","clacks") + emote_see = list("flaps its wings", "scratches the ground") + only_attack_enemies = TRUE + +/datum/mob_controller/aggressive/goose/retaliate(atom/source) + . = ..() + if(body?.stat == CONSCIOUS && istype(body, /mob/living/simple_animal/hostile/goose)) + var/mob/living/simple_animal/hostile/goose/goose = body + goose.enrage(goose.enrage_potency) + /obj/item/natural_weapon/goosefeet name = "goose feet" gender = PLURAL @@ -28,22 +37,20 @@ atom_damage_type = BRUTE canremove = FALSE -/mob/living/simple_animal/hostile/retaliate/goose/Retaliate() - ..() - if(stat == CONSCIOUS) - enrage(enrage_potency) +/mob/living/simple_animal/hostile/goose/get_door_pry_time() + return 8 SECONDS -/mob/living/simple_animal/hostile/retaliate/goose/on_update_icon() +/mob/living/simple_animal/hostile/goose/on_update_icon() ..() if(stat != DEAD && loose) icon_state += "-loose" -/mob/living/simple_animal/hostile/retaliate/goose/death(gibbed) +/mob/living/simple_animal/hostile/goose/death(gibbed) . = ..() if(. && !gibbed) update_icon() -/mob/living/simple_animal/hostile/retaliate/goose/proc/enrage(var/potency) +/mob/living/simple_animal/hostile/goose/proc/enrage(var/potency) var/obj/item/attacking_with = get_natural_weapon() if(attacking_with) attacking_with.force = min((attacking_with.force + potency), max_damage) @@ -56,7 +63,7 @@ desc += " The [name] is loose! Oh no!" update_icon() -/mob/living/simple_animal/hostile/retaliate/goose/dire +/mob/living/simple_animal/hostile/goose/dire name = "dire goose" desc = "A large bird. It radiates destructive energy." icon = 'icons/mob/simple_animal/goose_dire.dmi' 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 70342ca411d..bc582ff533b 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/jelly.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/jelly.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/hostile/retaliate/jelly +/mob/living/simple_animal/hostile/jelly name = "zeq" desc = "It looks like a floating jellyfish. How does it do that?" faction = "zeq" @@ -9,38 +9,45 @@ ) max_health = 75 natural_weapon = /obj/item/natural_weapon/tentacles - speak_chance = 0.5 - emote_see = list("wobbles slightly","oozes something out of tentacles' ends") + ai = /datum/mob_controller/aggressive/jelly base_movement_delay = 1 var/gets_random_color = TRUE +/datum/mob_controller/aggressive/jelly + speak_chance = 0.25 + emote_see = list("wobbles slightly","oozes something out of tentacles' ends") + only_attack_enemies = TRUE + /obj/item/natural_weapon/tentacles name = "tentacles" attack_verb = list("stung","slapped") force = 10 atom_damage_type = BURN -/mob/living/simple_animal/hostile/retaliate/jelly/Initialize() +/mob/living/simple_animal/hostile/jelly/Initialize() . = ..() if(gets_random_color) color = color_matrix_rotate_hue(round(rand(0,360),20)) -/mob/living/simple_animal/hostile/retaliate/jelly/alt +/mob/living/simple_animal/hostile/jelly/alt icon = 'icons/mob/simple_animal/jelly_alt.dmi' //megajellyfish -/mob/living/simple_animal/hostile/retaliate/jelly/mega +/mob/living/simple_animal/hostile/jelly/mega name = "zeq queen" desc = "A gigantic jellyfish-like creature. Its bell wobbles about almost as if it's ready to burst." max_health = 300 gets_random_color = FALSE - can_escape = TRUE + ai = /datum/mob_controller/hostile/megajelly var/jelly_scale = 3 - var/split_type = /mob/living/simple_animal/hostile/retaliate/jelly/mega/half + var/split_type = /mob/living/simple_animal/hostile/jelly/mega/half var/static/megajelly_color -/mob/living/simple_animal/hostile/retaliate/jelly/mega/Initialize() +/datum/mob_controller/hostile/megajelly + can_escape_buckles = TRUE + +/mob/living/simple_animal/hostile/jelly/mega/Initialize() . = ..() set_scale(jelly_scale) var/obj/item/attacking_with = get_natural_weapon() @@ -50,13 +57,13 @@ megajelly_color = color_matrix_rotate_hue(round(rand(0,360),20)) color = megajelly_color -/mob/living/simple_animal/hostile/retaliate/jelly/mega/death(gibbed) +/mob/living/simple_animal/hostile/jelly/mega/death(gibbed) if(split_type) jelly_split() return TRUE return ..() -/mob/living/simple_animal/hostile/retaliate/jelly/mega/proc/jelly_split() +/mob/living/simple_animal/hostile/jelly/mega/proc/jelly_split() visible_message(SPAN_MFAUNA("\The [src] rumbles briefly before splitting into two!")) var/kidnum = 2 for(var/i = 0 to kidnum) @@ -67,30 +74,29 @@ child.maxbodytemp = maxbodytemp QDEL_NULL(src) -/mob/living/simple_animal/hostile/retaliate/jelly/mega/half +/mob/living/simple_animal/hostile/jelly/mega/half name = "zeq duchess" desc = "A huge jellyfish-like creature." max_health = 150 - can_escape = TRUE jelly_scale = 1.5 - split_type = /mob/living/simple_animal/hostile/retaliate/jelly/mega/quarter + split_type = /mob/living/simple_animal/hostile/jelly/mega/quarter -/mob/living/simple_animal/hostile/retaliate/jelly/mega/quarter +/mob/living/simple_animal/hostile/jelly/mega/quarter name = "zeqling" desc = "A jellyfish-like creature." max_health = 75 jelly_scale = 0.75 - can_escape = FALSE - split_type = /mob/living/simple_animal/hostile/retaliate/jelly/mega/fourth + split_type = /mob/living/simple_animal/hostile/jelly/mega/fourth + ai = /datum/mob_controller/hostile -/mob/living/simple_animal/hostile/retaliate/jelly/mega/fourth +/mob/living/simple_animal/hostile/jelly/mega/fourth name = "zeqetta" desc = "A tiny jellyfish-like creature." max_health = 40 jelly_scale = 0.375 - split_type = /mob/living/simple_animal/hostile/retaliate/jelly/mega/eighth + split_type = /mob/living/simple_animal/hostile/jelly/mega/eighth -/mob/living/simple_animal/hostile/retaliate/jelly/mega/eighth +/mob/living/simple_animal/hostile/jelly/mega/eighth name = "zeqttina" desc = "An absolutely tiny jellyfish-like creature." max_health = 20 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 d18d66c3342..0f51b872c49 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 @@ -1,21 +1,41 @@ + +/datum/mob_controller/aggressive/goat/king + break_stuff_probability = 35 + emote_hear = list("brays in a booming voice") + emote_see = list("stamps a mighty foot, shaking the surroundings") + only_attack_enemies = TRUE + can_escape_buckles = TRUE + +/datum/mob_controller/aggressive/goat/king/retaliate(atom/source) + ..() + if(body?.stat == CONSCIOUS && prob(5)) + var/decl/pronouns/pronouns = body.get_pronouns() + body.visible_message(SPAN_WARNING("\The [body] bellows indignantly, with a judgemental gleam in [pronouns.his] eye.")) + +/datum/mob_controller/aggressive/goat/king/phase2 + break_stuff_probability = 40 + +/datum/mob_controller/aggressive/goat/king/phase2/retaliate(atom/source) + . = ..() + var/mob/living/simple_animal/hostile/goat/king/phase2/goat = body + if(istype(goat)) + goat.do_retaliation() + //Visager's tracks 'Battle!' and 'Miniboss Fight' from the album 'Songs from an Unmade World 2' are available here //http://freemusicarchive.org/music/Visager/Songs_From_An_Unmade_World_2/ and are made available under the CC BY 4.0 Attribution license, //which is available for viewing here: https://creativecommons.org/licenses/by/4.0/legalcode - //the king and his court -/mob/living/simple_animal/hostile/retaliate/goat/king +/mob/living/simple_animal/hostile/goat/king name = "king of goats" desc = "The oldest and wisest of goats; king of his race, peerless in dignity and power. His golden fleece radiates nobility." icon = 'icons/mob/simple_animal/goat_king.dmi' speak_emote = list("brays in a booming voice") - emote_hear = list("brays in a booming voice") - emote_see = list("stamps a mighty foot, shaking the surroundings") + ai = /datum/mob_controller/aggressive/goat/king response_harm = "assaults" max_health = 500 mob_size = MOB_SIZE_LARGE mob_bump_flag = HEAVY - can_escape = TRUE move_intents = list( /decl/move_intent/walk/animal, /decl/move_intent/run/animal @@ -23,7 +43,6 @@ min_gas = null max_gas = null minbodytemp = 0 - break_stuff_probability = 35 flash_protection = FLASH_PROTECTION_MAJOR natural_weapon = /obj/item/natural_weapon/goatking var/current_damtype = BRUTE @@ -33,7 +52,27 @@ ) var/stun_chance = 5 //chance per attack to Weaken target -/mob/living/simple_animal/hostile/retaliate/goat/king/get_natural_weapon() +/mob/living/simple_animal/hostile/goat/king/proc/OnDeath() + visible_message("\The [src] lets loose a terrific wail as its wounds close shut with a flash of light, and its eyes glow even brighter than before!") + new /mob/living/simple_animal/hostile/goat/king/phase2(src.loc) + qdel(src) + +/mob/living/simple_animal/hostile/goat/king/death(gibbed) + . = ..() + if(.) + OnDeath() + +/mob/living/simple_animal/hostile/goat/king/apply_attack_effects(mob/living/target) + . = ..() + if(prob(stun_chance)) + SET_STATUS_MAX(target, STAT_WEAK, 0.5) + ADJ_STATUS(target, STAT_CONFUSE, 1) + visible_message(SPAN_WARNING("\The [target] is bowled over by the impact of [src]'s attack!")) + +/mob/living/simple_animal/hostile/goat/king/Process_Spacemove() + return 1 + +/mob/living/simple_animal/hostile/goat/king/get_natural_weapon() if(!(current_damtype in elemental_weapons)) return ..() if(ispath(elemental_weapons[current_damtype])) @@ -55,7 +94,27 @@ name = "lightning horns" atom_damage_type = ELECTROCUTE -/mob/living/simple_animal/hostile/retaliate/goat/king/phase2 +/mob/living/simple_animal/hostile/goat/guard + name = "honour guard" + desc = "A very handsome and noble beast." + icon = 'icons/mob/simple_animal/goat_guard.dmi' + max_health = 125 + natural_weapon = /obj/item/natural_weapon/goathorns + +/obj/item/natural_weapon/goathorns + name = "horns" + attack_verb = list("impaled", "stabbed") + force = 15 + sharp = TRUE + +/mob/living/simple_animal/hostile/goat/guard/master + name = "master of the guard" + desc = "A very handsome and noble beast - the most trusted of all the king's men." + icon = 'icons/mob/simple_animal/goat_master.dmi' + max_health = 200 + natural_weapon = /obj/item/natural_weapon/goathorns + +/mob/living/simple_animal/hostile/goat/king/phase2 name = "emperor of goats" desc = "The King of Kings, God amongst men, and your superior in every way." icon = 'icons/mob/simple_animal/goat_king_phase_2.dmi' @@ -66,8 +125,8 @@ ELECTROCUTE = /obj/item/natural_weapon/goatking/lightning/unleashed ) default_pixel_y = 5 - break_stuff_probability = 40 stun_chance = 7 + ai = /datum/mob_controller/aggressive/goat/king/phase2 var/spellscast = 0 var/phase3 = FALSE @@ -84,7 +143,7 @@ /obj/item/natural_weapon/goatking/fire/unleashed force = 55 -/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/Initialize() +/mob/living/simple_animal/hostile/goat/king/phase2/Initialize() . = ..() boss_theme = play_looping_sound(src, sound_id, 'sound/music/Visager-Battle.ogg', volume = 10, range = 7, falloff = 4, prefer_mute = TRUE) update_icon() @@ -113,14 +172,8 @@ /decl/move_intent/run/animal ) -/mob/living/simple_animal/hostile/retaliate/goat/king/Retaliate() - ..() - if(stat == CONSCIOUS && prob(5)) - visible_message(SPAN_WARNING("The [src] bellows indignantly, with a judgemental gleam in his eye.")) - -/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/Retaliate() +/mob/living/simple_animal/hostile/goat/king/phase2/proc/do_retaliation() set waitfor = FALSE - ..() if(spellscast < 5) if(prob(5)) //stun move spellscast++ @@ -130,9 +183,9 @@ else if(prob(5)) //spawn adds spellscast++ visible_message(SPAN_MFAUNA("\The [src] summons the imperial guard to his aid, and they appear in a flash!")) - new /mob/living/simple_animal/hostile/retaliate/goat/guard/master(get_step(src,pick(global.cardinal))) - new /mob/living/simple_animal/hostile/retaliate/goat/guard(get_step(src,pick(global.cardinal))) - new /mob/living/simple_animal/hostile/retaliate/goat/guard(get_step(src,pick(global.cardinal))) + new /mob/living/simple_animal/hostile/goat/guard/master(get_step(src,pick(global.cardinal))) + new /mob/living/simple_animal/hostile/goat/guard(get_step(src,pick(global.cardinal))) + new /mob/living/simple_animal/hostile/goat/guard(get_step(src,pick(global.cardinal))) else if(prob(5)) //EMP blast spellscast++ @@ -150,7 +203,7 @@ else if(prob(5)) //earthquake spell visible_message("\The [src]' eyes begin to glow ominously as dust and debris in the area is kicked up in a light breeze.") - stop_automation = TRUE + ai?.pause() if(do_after(src, 6 SECONDS, src)) var/initial_brute = get_damage(BRUTE) var/initial_burn = get_damage(BURN) @@ -158,17 +211,16 @@ explosion(get_step(src,pick(global.cardinal)), -1, 2, 2, 3, 6) explosion(get_step(src,pick(global.cardinal)), -1, 1, 4, 4, 6) explosion(get_step(src,pick(global.cardinal)), -1, 3, 4, 3, 6) - stop_automation = FALSE spellscast += 2 set_damage(BRUTE, initial_brute) set_damage(BURN, initial_burn) else visible_message(SPAN_NOTICE("The [src] loses concentration and huffs haughtily.")) - stop_automation = FALSE + ai?.resume() else return -/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/proc/phase3_transition() +/mob/living/simple_animal/hostile/goat/king/phase2/proc/phase3_transition() phase3 = TRUE spellscast = 0 max_health = 750 @@ -180,7 +232,7 @@ update_icon() visible_message("\The [src]' wounds close with a flash, and when he emerges, he's even larger than before!") -/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/on_update_icon() +/mob/living/simple_animal/hostile/goat/king/phase2/on_update_icon() ..() if(phase3) icon_state += "-enraged" @@ -189,7 +241,7 @@ set_scale(1.25) default_pixel_y = 10 -/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/handle_living_non_stasis_processes() +/mob/living/simple_animal/hostile/goat/king/phase2/handle_living_non_stasis_processes() . = ..() if(!.) return FALSE @@ -199,39 +251,17 @@ if(current_health <= 150 && !phase3 && spellscast == 5) //begin phase 3, reset spell limit and heal phase3_transition() -/mob/living/simple_animal/hostile/retaliate/goat/king/proc/OnDeath() - visible_message("\The [src] lets loose a terrific wail as its wounds close shut with a flash of light, and its eyes glow even brighter than before!") - new /mob/living/simple_animal/hostile/retaliate/goat/king/phase2(src.loc) - qdel(src) - -/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/OnDeath() +/mob/living/simple_animal/hostile/goat/king/phase2/OnDeath() QDEL_NULL(boss_theme) if(phase3) visible_message(SPAN_MFAUNA("\The [src] shrieks as the seal on his power breaks and his wool sheds off!")) new /obj/item/towel/fleece(src.loc) -/mob/living/simple_animal/hostile/retaliate/goat/king/death(gibbed) - . = ..() - if(.) - OnDeath() - -/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/Destroy() +/mob/living/simple_animal/hostile/goat/king/phase2/Destroy() QDEL_NULL(boss_theme) . = ..() -/mob/living/simple_animal/hostile/retaliate/goat/king/attack_target(mob/target) - . = ..() - if(isliving(target_mob)) - var/mob/living/L = target_mob - if(prob(stun_chance)) - SET_STATUS_MAX(L, STAT_WEAK, 0.5) - ADJ_STATUS(L, STAT_CONFUSE, 1) - visible_message(SPAN_WARNING("\The [L] is bowled over by the impact of [src]'s attack!")) - -/mob/living/simple_animal/hostile/retaliate/goat/king/phase2/attack_target(mob/target) +/mob/living/simple_animal/hostile/goat/king/phase2/apply_attack_effects(mob/living/target) . = ..() if(current_damtype != BRUTE) special_attacks++ - -/mob/living/simple_animal/hostile/retaliate/goat/king/Process_Spacemove() - return 1 \ No newline at end of file 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 4a9ed00e327..114cfe2af24 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm @@ -24,24 +24,18 @@ #define PARROT_RETURN 32 //Flying towards its perch #define PARROT_FLEE 64 //Flying away from its attacker - -/mob/living/simple_animal/hostile/retaliate/parrot +/mob/living/simple_animal/hostile/parrot name = "parrot" desc = "A large, colourful tropical bird native to Earth, known for its strong beak and ability to mimic speech." icon = 'icons/mob/simple_animal/parrot.dmi' pass_flags = PASS_FLAG_TABLE mob_size = MOB_SIZE_SMALL - emote_speech = list("Hi","Hello!","Cracker?") speak_emote = list("squawks","says","yells") - emote_hear = list("squawks","bawks") - 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_wander = 5 response_harm = "swats" - stop_wandering = TRUE universal_speak = TRUE butchery_data = /decl/butchery_data/animal/bird/parrot + ai = /datum/mob_controller/aggressive/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. @@ -55,6 +49,8 @@ //These vars store their preffered perch and if they dont have one, what they can use as a perch var/obj/parrot_perch = null var/static/list/desired_perches = list( + /obj/structure/bed/chair, + /obj/structure/table, /obj/machinery/constructable_frame/computerframe, /obj/structure/displaycase, /obj/structure/filing_cabinet, @@ -78,274 +74,287 @@ var/impatience = 5 //we lose this much from relax_chance each time we calm down var/icon_set = "parrot" -/mob/living/simple_animal/hostile/retaliate/parrot/Initialize() - . = ..() - - parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var - - verbs |= /mob/living/simple_animal/hostile/retaliate/parrot/proc/steal_from_ground - verbs |= /mob/living/simple_animal/hostile/retaliate/parrot/proc/steal_from_mob - verbs |= /mob/living/simple_animal/hostile/retaliate/parrot/verb/drop_held_item_player - verbs |= /mob/living/simple_animal/hostile/retaliate/parrot/proc/perch_player - - update_icon() - -/mob/living/simple_animal/hostile/retaliate/parrot/Destroy() - parrot_interest = null - parrot_perch = null - if(held_item) - held_item.dropInto(loc) - held_item = null - return ..() - -/mob/living/simple_animal/hostile/retaliate/parrot/death(gibbed) - var/oldloc = loc - . = ..() - if(. && held_item) - if(oldloc) - held_item.dropInto(oldloc) - held_item = null - else - QDEL_NULL(held_item) - -/mob/living/simple_animal/hostile/retaliate/parrot/Stat() - . = ..() - stat("Held Item", held_item) - -/* - * Attack responces - */ -//Humans, monkeys, aliens -/mob/living/simple_animal/hostile/retaliate/parrot/default_hurt_interaction(mob/user) - . = ..() - if(!client && !simple_parrot && !stat) - 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... - if(isliving(user)) - var/mob/living/M = user - if(M.current_health < 50) //Weakened mob? Fight back! - parrot_state |= PARROT_ATTACK - return - parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell! - drop_held_item(0) - update_icon() - -//Mobs with objects -/mob/living/simple_animal/hostile/retaliate/parrot/attackby(var/obj/item/O, var/mob/user) - ..() - if(!stat && !client && !istype(O, /obj/item/stack/medical)) - if(O.force) - 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 | PARROT_FLEE - drop_held_item(0) - update_icon() - -//Bullets -/mob/living/simple_animal/hostile/retaliate/parrot/bullet_act(var/obj/item/projectile/Proj) - ..() - if(!stat && !client) - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - parrot_interest = null - parrot_state = PARROT_WANDER //OWFUCK, Been shot! RUN LIKE HELL! - parrot_been_shot += 5 - drop_held_item(0) - update_icon() +/datum/mob_controller/aggressive/parrot + turns_per_wander = 10 + emote_speech = list("Hi","Hello!","Cracker?") + emote_hear = list("squawks","bawks") + emote_see = list("flutters its wings") + do_wander = FALSE + speak_chance = 0.5 + only_attack_enemies = TRUE + expected_type = /mob/living/simple_animal/hostile/parrot /* * AI - Not really intelligent, but I'm calling it AI anyway. */ +/datum/mob_controller/aggressive/parrot/do_process() + . = ..() + if(!body || body.stat || !istype(body, /mob/living/simple_animal/hostile/parrot)) + return + var/mob/living/simple_animal/hostile/parrot/parrot = body -// This has the potential to sleep in various emote and damage procs; shoving it all into here for safety. -/mob/living/simple_animal/hostile/retaliate/parrot/do_delayed_life_action() - ..() - if(!isturf(src.loc) || stat) + if(!isturf(parrot.loc)) return // Let's not bother in nullspace - if(enemies.len && prob(relax_chance)) - give_up() - if(simple_parrot) + if(LAZYLEN(get_enemies()) && prob(parrot.relax_chance)) + parrot.give_up() + + if(parrot.simple_parrot) return FALSE //-----SLEEPING - if(parrot_state == PARROT_PERCH) - if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasnt moved our perch on us - if(parrot_perch in view(src)) - parrot_state = PARROT_SWOOP | PARROT_RETURN + if(parrot.parrot_state == PARROT_PERCH) + if(parrot.parrot_perch && parrot.parrot_perch.loc != parrot.loc) //Make sure someone hasnt moved our perch on us + if(parrot.parrot_perch in view(parrot)) + parrot.parrot_state = PARROT_SWOOP | PARROT_RETURN else - parrot_state = PARROT_WANDER - update_icon() + parrot.parrot_state = PARROT_WANDER + parrot.update_icon() return - if(--parrot_sleep_dur) //Zzz + if(--parrot.parrot_sleep_dur) //Zzz return else //This way we only call the stuff below once every [sleep_max] ticks. - parrot_sleep_dur = parrot_sleep_max + parrot.parrot_sleep_dur = parrot.parrot_sleep_max //Search for item to steal - parrot_interest = search_for_item() - if(parrot_interest) - visible_emote("looks in [parrot_interest]'s direction and takes flight") - parrot_state = PARROT_SWOOP | PARROT_STEAL - update_icon() + parrot.parrot_interest = parrot.search_for_item() + if(parrot.parrot_interest) + parrot.visible_emote("looks in [parrot.parrot_interest]'s direction and takes flight") + parrot.parrot_state = PARROT_SWOOP | PARROT_STEAL + parrot.update_icon() //-----WANDERING - This is basically a 'I dont know what to do yet' state - else if(parrot_state == PARROT_WANDER) + else if(parrot.parrot_state == PARROT_WANDER) //Stop movement, we'll set it later - stop_automove() - parrot_interest = null + parrot.stop_automove() + parrot.parrot_interest = null //Wander around aimlessly. This will help keep the loops from searches down //and possibly move the mob into a new are in view of something they can use if(prob(90)) - SelfMove(pick(global.cardinal)) + parrot.SelfMove(pick(global.cardinal)) return - 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(!parrot.held_item && !parrot.parrot_perch) //If we've got nothing to do, look for something to do. + var/atom/movable/AM = parrot.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 - parrot_interest = AM - visible_emote("turns and flies towards [parrot_interest]") - parrot_state = PARROT_SWOOP | PARROT_STEAL + if((isitem(AM) && parrot.can_pick_up(AM)) || isliving(AM)) //If stealable item + parrot.parrot_interest = AM + parrot.visible_emote("turns and flies towards [parrot.parrot_interest]") + parrot.parrot_state = PARROT_SWOOP | PARROT_STEAL return else //Else it's a perch - parrot_perch = AM - parrot_state = PARROT_SWOOP | PARROT_RETURN + parrot.parrot_perch = AM + parrot.parrot_state = PARROT_SWOOP | PARROT_RETURN return return - if(parrot_interest && (parrot_interest in view(src))) - parrot_state = PARROT_SWOOP | PARROT_STEAL + if(parrot.parrot_interest && (parrot.parrot_interest in view(parrot))) + parrot.parrot_state = PARROT_SWOOP | PARROT_STEAL return - if(parrot_perch && (parrot_perch in view(src))) - parrot_state = PARROT_SWOOP | PARROT_RETURN + if(parrot.parrot_perch && (parrot.parrot_perch in view(parrot))) + parrot.parrot_state = PARROT_SWOOP | PARROT_RETURN return else //Have an item but no perch? Find one! - parrot_perch = search_for_perch() - if(parrot_perch) - parrot_state = PARROT_SWOOP | PARROT_RETURN + parrot.parrot_perch = parrot.search_for_perch() + if(parrot.parrot_perch) + parrot.parrot_state = PARROT_SWOOP | PARROT_RETURN return //-----STEALING - else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL)) - stop_automove() - if(!parrot_interest || held_item) - parrot_state = PARROT_SWOOP | PARROT_RETURN + else if(parrot.parrot_state == (PARROT_SWOOP | PARROT_STEAL)) + parrot.stop_automove() + if(!parrot.parrot_interest || parrot.held_item) + parrot.parrot_state = PARROT_SWOOP | PARROT_RETURN return - if(!(parrot_interest in view(src))) - parrot_state = PARROT_SWOOP | PARROT_RETURN + if(!(parrot.parrot_interest in view(parrot))) + parrot.parrot_state = PARROT_SWOOP | PARROT_RETURN return - if(in_range(src, parrot_interest)) + if(in_range(parrot, parrot.parrot_interest)) - if(isliving(parrot_interest)) - steal_from_mob() + if(isliving(parrot.parrot_interest)) + parrot.steal_from_mob() - if(isitem(parrot_interest) && can_pick_up(parrot_interest))//This should ensure that we only grab the item we want, and make sure it's not already collected on our perch, a correct size, and not bolted to the floor - if(!parrot_perch || parrot_interest.loc != parrot_perch.loc) - held_item = parrot_interest - parrot_interest.forceMove(src) - visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") + if(isitem(parrot.parrot_interest) && parrot.can_pick_up(parrot.parrot_interest))//This should ensure that we only grab the item we want, and make sure it's not already collected on our perch, a correct size, and not bolted to the floor + if(!parrot.parrot_perch || parrot.parrot_interest.loc != parrot.parrot_perch.loc) + parrot.held_item = parrot.parrot_interest + parrot.parrot_interest.forceMove(parrot) + parrot.visible_message("[parrot] grabs the [parrot.held_item]!", "You grab the [parrot.held_item]!", "You hear the sounds of wings flapping furiously.") - parrot_interest = null - parrot_state = PARROT_SWOOP | PARROT_RETURN + parrot.parrot_interest = null + parrot.parrot_state = PARROT_SWOOP | PARROT_RETURN return - set_moving_slowly() - start_automove(parrot_interest) + parrot.set_moving_slowly() + parrot.start_automove(parrot.parrot_interest) return //-----RETURNING TO PERCH - else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN)) - 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 + else if(parrot.parrot_state == (PARROT_SWOOP | PARROT_RETURN)) + parrot.stop_automove() + if(!parrot.parrot_perch || !isturf(parrot.parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else. + parrot.parrot_perch = null + parrot.parrot_state = PARROT_WANDER return - if(in_range(src, parrot_perch)) - forceMove(parrot_perch.loc) - drop_held_item() - parrot_state = PARROT_PERCH - update_icon() + if(in_range(parrot, parrot.parrot_perch)) + parrot.forceMove(parrot.parrot_perch.loc) + parrot.drop_held_item() + parrot.parrot_state = PARROT_PERCH + parrot.update_icon() return - set_moving_slowly() - start_automove(parrot_perch) + parrot.set_moving_slowly() + parrot.start_automove(parrot.parrot_perch) return //-----FLEEING - else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE)) - stop_automove() - give_up() - if(!parrot_interest || !isliving(parrot_interest)) //Sanity - parrot_state = PARROT_WANDER + else if(parrot.parrot_state == (PARROT_SWOOP | PARROT_FLEE)) + parrot.stop_automove() + parrot.give_up() + if(!parrot.parrot_interest || !isliving(parrot.parrot_interest)) //Sanity + parrot.parrot_state = PARROT_WANDER 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-- + parrot.set_moving_quickly() + parrot.start_automove(parrot.parrot_interest, metadata = _parrot_flee_automove_metadata) + parrot.parrot_been_shot-- return //-----ATTACKING - else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK)) + else if(parrot.parrot_state == (PARROT_SWOOP | PARROT_ATTACK)) //If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander - if(!parrot_interest || !isliving(parrot_interest)) - parrot_interest = null - parrot_state = PARROT_WANDER + if(!parrot.parrot_interest || !isliving(parrot.parrot_interest)) + parrot.parrot_interest = null + parrot.parrot_state = PARROT_WANDER return - var/mob/living/L = parrot_interest + var/mob/living/L = parrot.parrot_interest //If the mob is close enough to interact with - if(in_range(src, parrot_interest)) + if(in_range(parrot, parrot.parrot_interest)) //If the mob we've been chasing/attacking dies or falls into crit, check for loot! if(L.stat) - parrot_interest = null - if(!held_item) - held_item = steal_from_ground() - if(!held_item) - held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances. - if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home - parrot_state = PARROT_SWOOP | PARROT_RETURN + parrot.parrot_interest = null + if(!parrot.held_item) + parrot.held_item = parrot.steal_from_ground() + if(!parrot.held_item) + parrot.held_item = parrot.steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances. + if(parrot.parrot_perch in view(parrot)) //If we have a home nearby, go to it, otherwise find a new home + parrot.parrot_state = PARROT_SWOOP | PARROT_RETURN else - parrot_state = PARROT_WANDER + parrot.parrot_state = PARROT_WANDER return //Time for the hurt to begin! - UnarmedAttack(L) + parrot.UnarmedAttack(L) return //Otherwise, fly towards the mob! else - set_moving_quickly() - start_automove(parrot_interest) + parrot.set_moving_quickly() + parrot.start_automove(parrot.parrot_interest) return //-----STATE MISHAP else //This should not happen. If it does lets reset everything and try again - stop_automove() - parrot_interest = null - parrot_perch = null - drop_held_item() - parrot_state = PARROT_WANDER + parrot.stop_automove() + parrot.parrot_interest = null + parrot.parrot_perch = null + parrot.drop_held_item() + parrot.parrot_state = PARROT_WANDER return -/mob/living/simple_animal/hostile/retaliate/parrot/proc/search_for_item() +/mob/living/simple_animal/hostile/parrot/Initialize() + . = ..() + + parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var + + verbs |= /mob/living/simple_animal/hostile/parrot/proc/steal_from_ground + verbs |= /mob/living/simple_animal/hostile/parrot/proc/steal_from_mob + verbs |= /mob/living/simple_animal/hostile/parrot/verb/drop_held_item_player + verbs |= /mob/living/simple_animal/hostile/parrot/proc/perch_player + + update_icon() + +/mob/living/simple_animal/hostile/parrot/Destroy() + parrot_interest = null + parrot_perch = null + if(held_item) + held_item.dropInto(loc) + held_item = null + return ..() + +/mob/living/simple_animal/hostile/parrot/death(gibbed) + var/oldloc = loc + . = ..() + if(. && held_item) + if(oldloc) + held_item.dropInto(oldloc) + held_item = null + else + QDEL_NULL(held_item) + +/mob/living/simple_animal/hostile/parrot/Stat() + . = ..() + stat("Held Item", held_item) + +/* + * Attack responces + */ +//Humans, monkeys, aliens +/mob/living/simple_animal/hostile/parrot/default_hurt_interaction(mob/user) + . = ..() + if(!client && !simple_parrot && !stat) + 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.. + if(isliving(user)) + var/mob/living/M = user + if(M.current_health < 50) //Weakened mob? Fight back! + parrot_state |= PARROT_ATTACK + return + parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell! + drop_held_item(0) + update_icon() + +//Mobs with objects +/mob/living/simple_animal/hostile/parrot/attackby(var/obj/item/O, var/mob/user) + ..() + if(!stat && !client && !istype(O, /obj/item/stack/medical)) + if(O.force) + 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 | PARROT_FLEE + drop_held_item(0) + update_icon() + +//Bullets +/mob/living/simple_animal/hostile/parrot/bullet_act(var/obj/item/projectile/Proj) + ..() + if(!stat && !client) + if(parrot_state == PARROT_PERCH) + parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched + parrot_interest = null + parrot_state = PARROT_WANDER //OWFUCK, Been shot! RUN LIKE HELL! + parrot_been_shot += 5 + drop_held_item(0) + update_icon() + +/mob/living/simple_animal/hostile/parrot/proc/search_for_item() for(var/atom/movable/AM in view(src)) //Skip items we already stole or are wearing or are too big if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) @@ -362,7 +371,7 @@ if(held && can_pick_up(held)) return M -/mob/living/simple_animal/hostile/retaliate/parrot/proc/search_for_perch() +/mob/living/simple_animal/hostile/parrot/proc/search_for_perch() for(var/obj/O in view(src)) for(var/path in desired_perches) if(istype(O, path)) @@ -370,7 +379,7 @@ return null //This proc was made to save on doing two 'in view' loops seperatly -/mob/living/simple_animal/hostile/retaliate/parrot/proc/search_for_perch_and_item() +/mob/living/simple_animal/hostile/parrot/proc/search_for_perch_and_item() for(var/atom/movable/AM in view(src)) for(var/perch_path in desired_perches) if(istype(AM, perch_path)) @@ -391,18 +400,18 @@ if(held && can_pick_up(held)) return M -/mob/living/simple_animal/hostile/retaliate/parrot/proc/give_up() - if(!length(enemies)) +/mob/living/simple_animal/hostile/parrot/proc/give_up() + if(!istype(ai) || !LAZYLEN(ai.get_enemies())) return - enemies = list() - LoseTarget() + ai.clear_enemies() + ai.lose_target() visible_message(SPAN_NOTICE("\The [src] seems to calm down.")) relax_chance -= impatience /* * Verbs - These are actually procs, but can be used as verbs by player-controlled parrots. */ -/mob/living/simple_animal/hostile/retaliate/parrot/proc/steal_from_ground() +/mob/living/simple_animal/hostile/parrot/proc/steal_from_ground() set name = "Steal from ground" set category = "Parrot" set desc = "Grabs a nearby item." @@ -430,7 +439,7 @@ to_chat(src, "There is nothing of interest to take.") return 0 -/mob/living/simple_animal/hostile/retaliate/parrot/proc/steal_from_mob() +/mob/living/simple_animal/hostile/parrot/proc/steal_from_mob() set name = "Steal from mob" set category = "Parrot" set desc = "Steals an item right out of a person's hand!" @@ -456,7 +465,7 @@ to_chat(src, "There is nothing of interest to take.") return 0 -/mob/living/simple_animal/hostile/retaliate/parrot/verb/drop_held_item_player() +/mob/living/simple_animal/hostile/parrot/verb/drop_held_item_player() set name = "Drop held item" set category = "Parrot" set desc = "Drop the item you're holding." @@ -468,7 +477,7 @@ return -/mob/living/simple_animal/hostile/retaliate/parrot/proc/drop_held_item(var/drop_gently = 1) +/mob/living/simple_animal/hostile/parrot/proc/drop_held_item(var/drop_gently = 1) set name = "Drop held item" set category = "Parrot" set desc = "Drop the item you're holding." @@ -495,7 +504,7 @@ held_item = null return 1 -/mob/living/simple_animal/hostile/retaliate/parrot/proc/perch_player() +/mob/living/simple_animal/hostile/parrot/proc/perch_player() set name = "Sit" set category = "Parrot" set desc = "Sit on a nice comfy perch." @@ -511,13 +520,16 @@ return to_chat(src, SPAN_WARNING("There is no perch nearby to sit on.")) -/mob/living/simple_animal/hostile/retaliate/parrot/proc/can_pick_up(obj/item/I) +/mob/living/simple_animal/hostile/parrot/proc/can_pick_up(obj/item/I) . = (Adjacent(I) && I.w_class <= parrot_isize && !I.anchored) /* * Sub-types */ -/mob/living/simple_animal/hostile/retaliate/parrot/Poly +/mob/living/simple_animal/hostile/parrot/Poly name = "Poly" desc = "Poly the Parrot. An expert on quantum cracker theory." + ai = /datum/mob_controller/aggressive/parrot/poly + +/datum/mob_controller/aggressive/parrot/poly emote_speech = list("Poly wanna cracker!", "Check the singlo, you chucklefucks!","Wire the solars, you lazy bums!","WHO TOOK THE DAMN HARDSUITS?","OH GOD ITS FREE CALL THE SHUTTLE!") diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm deleted file mode 100644 index fc90845fd85..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm +++ /dev/null @@ -1,58 +0,0 @@ -/mob/living/simple_animal/hostile/retaliate - var/list/enemies = list() - -/mob/living/simple_animal/hostile/can_direct_mount(mob/user) - return user?.faction == faction && ..() - -/mob/living/simple_animal/hostile/retaliate/Destroy() - LAZYCLEARLIST(enemies) - return ..() - -/mob/living/simple_animal/hostile/retaliate/Found(var/atom/A) - if(isliving(A)) - var/mob/living/L = A - if(!L.stat) - stance = HOSTILE_STANCE_ATTACK - return L - else - enemies -= weakref(L) - -/mob/living/simple_animal/hostile/retaliate/ListTargets(var/dist = 7) - if(!length(enemies)) - return - . = ..() - if(length(.)) - var/list/filtered_enemies = list() - for(var/weakref/enemy in enemies) // Remove all entries that aren't in enemies - var/M = enemy.resolve() - if(M in .) - filtered_enemies += M - . = filtered_enemies - -/mob/living/simple_animal/hostile/retaliate/proc/Retaliate() - var/list/around = view(src, 7) - - for(var/atom/movable/A in around) - if(A == src) - continue - if(isliving(A)) - var/mob/living/M = A - if(!attack_same && M.faction != faction) - enemies |= weakref(M) - - for(var/mob/living/simple_animal/hostile/retaliate/H in around) - if(!attack_same && !H.attack_same && H.faction == faction) - H.enemies |= enemies - return 0 - -/mob/living/simple_animal/hostile/retaliate/adjustBruteLoss(var/damage, var/do_update_health = FALSE) - ..() - Retaliate() - -/mob/living/simple_animal/hostile/retaliate/buckle_mob(mob/living/M) - . = ..() - Retaliate() - -/mob/living/simple_animal/hostile/retaliate/try_make_grab(mob/living/user, defer_hand = FALSE) - . = ..() - Retaliate() diff --git a/code/modules/mob/living/simple_animal/hostile/revenant.dm b/code/modules/mob/living/simple_animal/hostile/revenant.dm new file mode 100644 index 00000000000..6372c01a1f7 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/revenant.dm @@ -0,0 +1,52 @@ +/mob/living/simple_animal/hostile/revenant + name = "revenant" + desc = "A flickering humanoid shadow that exudes a palpable sense of mance." + icon = 'icons/mob/simple_animal/revenant.dmi' + 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 + ai = /datum/mob_controller/aggressive/revenant + + harm_intent_damage = 10 + natural_weapon = /obj/item/natural_weapon/revenant + + min_gas = null + max_gas = null + minbodytemp = 0 + + faction = "revenants" + supernatural = 1 + +/datum/mob_controller/aggressive/revenant + speak_chance = 0 + turns_per_wander = 10 + +/datum/mob_controller/aggressive/revenant/find_target() + . = ..() + if(.) + body.custom_emote(AUDIBLE_MESSAGE, "wails at [.]") + +/obj/item/natural_weapon/revenant + name = "shadow tendril" + attack_verb = list("gripped") + hitsound = 'sound/hallucinations/growl1.ogg' + atom_damage_type = BURN + force = 15 + +/mob/living/simple_animal/hostile/revenant/Process_Spacemove() + return 1 + +/mob/living/simple_animal/hostile/revenant/apply_attack_effects(mob/living/target) + . = ..() + if(prob(12)) + SET_STATUS_MAX(target, STAT_WEAK, 3) + target.visible_message(SPAN_DANGER("\The [src] knocks down \the [target]!")) + +/obj/item/ectoplasm + name = "ectoplasm" + desc = "Spooky." + gender = PLURAL + icon = 'icons/obj/items/ectoplasm.dmi' + icon_state = ICON_STATE_WORLD + material = /decl/material/liquid/drink/compote \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/slug.dm b/code/modules/mob/living/simple_animal/hostile/slug.dm index ac4e93dd497..8384fc93a6c 100644 --- a/code/modules/mob/living/simple_animal/hostile/slug.dm +++ b/code/modules/mob/living/simple_animal/hostile/slug.dm @@ -4,7 +4,6 @@ desc = "A vicious, viscous little creature, it has a mouth of too many teeth and a penchant for blood." icon = 'icons/mob/simple_animal/slug.dmi' response_harm = "stomps on" - destroy_surroundings = 0 max_health = 15 move_intents = list( /decl/move_intent/walk/animal_fast, @@ -13,21 +12,27 @@ density = TRUE min_gas = null mob_size = MOB_SIZE_MINISCULE - can_escape = TRUE pass_flags = PASS_FLAG_TABLE natural_weapon = /obj/item/natural_weapon/bite holder_type = /obj/item/holder/slug + ai = /datum/mob_controller/aggressive/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 +/datum/mob_controller/aggressive/slug + try_destroy_surroundings = FALSE + can_escape_buckles = TRUE -/mob/living/simple_animal/hostile/slug/ListTargets(var/dist = 7) +/datum/mob_controller/aggressive/slug/list_targets(var/dist = 7) . = ..() - for(var/mob/living/M in .) - if(check_friendly_species(M)) - . -= M + var/mob/living/simple_animal/hostile/slug/slug = body + if(istype(slug)) + for(var/mob/living/M in .) + if(slug.check_friendly_species(M)) + . -= M + +/mob/living/simple_animal/hostile/slug/proc/check_friendly_species(var/mob/living/M) + return istype(M) && M.faction == faction /mob/living/simple_animal/hostile/slug/get_scooped(var/mob/living/target, var/mob/living/initiator) if(target == initiator || check_friendly_species(initiator)) @@ -46,10 +51,10 @@ chest.embed_in_organ(holder, FALSE, "\The [src] latches itself onto \the [H]!") holder.sync(src) -/mob/living/simple_animal/hostile/slug/attack_target(mob/target) +/mob/living/simple_animal/hostile/slug/apply_attack_effects(mob/living/target) . = ..() - if(ishuman(.)) - var/mob/living/human/H = . + if(ishuman(target)) + var/mob/living/human/H = target if(prob(H.get_damage(BRUTE)/2)) attach(H) diff --git a/code/modules/mob/living/simple_animal/hostile/drake.dm b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm similarity index 55% rename from code/modules/mob/living/simple_animal/hostile/drake.dm rename to code/modules/mob/living/simple_animal/hostile/space_dragon.dm index ef7cdc3e5ca..7aeb001cb3b 100644 --- a/code/modules/mob/living/simple_animal/hostile/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm @@ -1,18 +1,14 @@ -/mob/living/simple_animal/hostile/drake - name = "drake" +/mob/living/simple_animal/hostile/space_dragon + name = "space dragon" desc = "A large reptilian creature, with vicious looking claws." - icon = 'icons/mob/simple_animal/drake.dmi' + icon = 'icons/mob/simple_animal/space_dragon.dmi' mob_size = MOB_SIZE_LARGE speak_emote = list("hisses") - emote_hear = list("clicks") - emote_see = list("flaps its wings idly") - break_stuff_probability = 15 - faction = "drakes" - pry_time = 4 SECONDS - butchery_data = /decl/butchery_data/animal/reptile/drake + faction = "space dragons" + butchery_data = /decl/butchery_data/animal/reptile/space_dragon bleed_colour = COLOR_VIOLET max_health = 200 - natural_weapon = /obj/item/natural_weapon/claws/drake + natural_weapon = /obj/item/natural_weapon/claws/space_dragon natural_armor = list( ARMOR_MELEE = ARMOR_MELEE_RESISTANT, ARMOR_ENERGY = ARMOR_ENERGY_SHIELDED, @@ -20,30 +16,39 @@ ARMOR_BOMB = ARMOR_BOMB_SHIELDED ) ability_cooldown = 80 SECONDS + ai = /datum/mob_controller/aggressive/space_dragon var/obj/item/whip/tail/tailwhip var/empowered_attack = FALSE var/gas_spent = FALSE -/mob/living/simple_animal/hostile/drake/lava_act(datum/gas_mixture/air, temperature, pressure) +/datum/mob_controller/aggressive/space_dragon + emote_hear = list("clicks") + break_stuff_probability = 15 + emote_see = list("flaps its wings idly") + +/mob/living/simple_animal/hostile/space_dragon/get_door_pry_time() + return 4 SECONDS + +/mob/living/simple_animal/hostile/space_dragon/lava_act(datum/gas_mixture/air, temperature, pressure) return -/mob/living/simple_animal/hostile/drake/attack_target(mob/target) +/mob/living/simple_animal/hostile/space_dragon/apply_attack_effects(mob/living/target) . = ..() if(empowered_attack) depower() return - if(can_act() && !is_on_special_ability_cooldown() && target_mob) + if(can_act() && !is_on_special_ability_cooldown() && ai?.get_target()) empower() -/mob/living/simple_animal/hostile/drake/get_natural_weapon() +/mob/living/simple_animal/hostile/space_dragon/get_natural_weapon() if(empowered_attack) if(!tailwhip) tailwhip = new(src) return tailwhip . = ..() -/mob/living/simple_animal/hostile/drake/proc/empower() +/mob/living/simple_animal/hostile/space_dragon/proc/empower() visible_message(SPAN_MFAUNA("\The [src] thrashes its tail about!")) empowered_attack = TRUE if(prob(25) && !gas_spent) @@ -52,15 +57,15 @@ return set_special_ability_cooldown(ability_cooldown) -/mob/living/simple_animal/hostile/drake/proc/vent_gas() +/mob/living/simple_animal/hostile/space_dragon/proc/vent_gas() visible_message(SPAN_MFAUNA("\The [src] raises its wings, vents a miasma of burning gas, and spreads it about with a flap!")) gas_spent = TRUE for(var/mob/living/L in oview(2)) var/obj/item/projectile/P = new /obj/item/projectile/hotgas(get_turf(src)) P.launch(L) -/mob/living/simple_animal/hostile/drake/proc/depower() +/mob/living/simple_animal/hostile/space_dragon/proc/depower() empowered_attack = FALSE -/obj/item/natural_weapon/claws/drake +/obj/item/natural_weapon/claws/space_dragon force = 15 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/tree.dm b/code/modules/mob/living/simple_animal/hostile/tree.dm index ecddb081881..325ca3f6f8a 100644 --- a/code/modules/mob/living/simple_animal/hostile/tree.dm +++ b/code/modules/mob/living/simple_animal/hostile/tree.dm @@ -2,28 +2,32 @@ name = "pine tree" 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_wander = 5 butchery_data = null max_health = 250 pixel_x = -16 harm_intent_damage = 5 natural_weapon = /obj/item/natural_weapon/bite + ai = /datum/mob_controller/aggressive/tree base_movement_delay = -1 + // For 12 years, this comment has implied trees are a kind of space carp. //Space carp aren't affected by atmos. min_gas = null max_gas = null minbodytemp = 0 faction = "carp" -/mob/living/simple_animal/hostile/tree/check_has_mouth() - return FALSE +/datum/mob_controller/aggressive/tree + speak_chance = 0 + turns_per_wander = 10 -/mob/living/simple_animal/hostile/tree/FindTarget() +/datum/mob_controller/aggressive/tree/find_target() . = ..() if(.) - audible_emote("growls at [.]") + body.custom_emote(AUDIBLE_MESSAGE, "growls at [.]") + +/mob/living/simple_animal/hostile/tree/check_has_mouth() + return FALSE /mob/living/simple_animal/hostile/tree/get_death_message(gibbed) return "is hacked into pieces!" diff --git a/code/modules/mob/living/simple_animal/hostile/vagrant.dm b/code/modules/mob/living/simple_animal/hostile/vagrant.dm index bbff171e105..3ceb6b57681 100644 --- a/code/modules/mob/living/simple_animal/hostile/vagrant.dm +++ b/code/modules/mob/living/simple_animal/hostile/vagrant.dm @@ -1,16 +1,12 @@ - /mob/living/simple_animal/hostile/vagrant name = "creature" desc = "You get the feeling you should run." icon = 'icons/mob/simple_animal/vagrant.dmi' max_health = 60 - speak_chance = 0 - 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 natural_weapon = /obj/item/natural_weapon/bite/weak @@ -22,6 +18,7 @@ pass_flags = PASS_FLAG_TABLE bleed_colour = "#aad9de" nutrition = 100 + ai = /datum/mob_controller/aggressive/vagrant base_movement_delay = 5 var/cloaked = 0 @@ -29,16 +26,21 @@ var/blood_per_tick = 3 var/health_per_tick = 0.8 +/datum/mob_controller/aggressive/vagrant + speak_chance = 0 + turns_per_wander = 8 + break_stuff_probability = 0 + /mob/living/simple_animal/hostile/vagrant/Process_Spacemove() return 1 /mob/living/simple_animal/hostile/vagrant/bullet_act(var/obj/item/projectile/Proj) var/oldhealth = current_health . = ..() - 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_wander = 3 - MoveToTarget() + if(istype(ai) && isliving(Proj.firer) && (ai.get_target() != Proj.firer) && current_health < oldhealth && !incapacitated(INCAPACITATION_KNOCKOUT)) //Respond to being shot at + ai.set_target(Proj.firer) + ai.turns_per_wander = 6 + ai.move_to_target() /mob/living/simple_animal/hostile/vagrant/death(gibbed) . = ..() @@ -63,10 +65,12 @@ else gripping = null - if(turns_per_wander != initial(turns_per_wander)) - turns_per_wander = initial(turns_per_wander) + // I suspect the original coder mistook this var for movement delay. + // Changing wander time makes no sense in this context. + if(istype(ai) && ai.turns_per_wander != initial(ai.turns_per_wander)) + ai.turns_per_wander = initial(ai.turns_per_wander) - if(stance == HOSTILE_STANCE_IDLE && !cloaked) + if(istype(ai) && ai.get_stance() == STANCE_IDLE && !cloaked) cloaked = 1 update_icon() @@ -89,10 +93,10 @@ set_light(3, 0.2) set_moving_quickly() -/mob/living/simple_animal/hostile/vagrant/attack_target(mob/target) +/mob/living/simple_animal/hostile/vagrant/apply_attack_effects(mob/living/target) . = ..() - if(ishuman(.)) - var/mob/living/human/H = . + if(ishuman(target)) + var/mob/living/human/H = target if(gripping == H) SET_STATUS_MAX(H, STAT_WEAK, 1) SET_STATUS_MAX(H, STAT_STUN, 1) diff --git a/code/modules/mob/living/simple_animal/passive/_passive.dm b/code/modules/mob/living/simple_animal/passive/_passive.dm index c9d3cb99c48..7920e9589ce 100644 --- a/code/modules/mob/living/simple_animal/passive/_passive.dm +++ b/code/modules/mob/living/simple_animal/passive/_passive.dm @@ -1,92 +1,7 @@ -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/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))) - critter.set_moving_quickly() - critter.start_automove(flee_target_atom, metadata = global._flee_automove_metadata) - critter.stop_wandering = TRUE - else - flee_target = null - critter.set_moving_slowly() - critter.stop_wandering = FALSE - return !isnull(flee_target) - -/datum/mob_controller/passive/do_process(time_elapsed) - ..() - - // Handle fleeing from aggressors. - turns_since_scan++ - if (turns_since_scan > 5) - body.stop_automove() - turns_since_scan = 0 - if(update_targets()) - return - - var/mob/living/simple_animal/critter = body - // Handle sleeping or wandering. - if(body.stat == CONSCIOUS && prob(0.5)) - body.set_stat(UNCONSCIOUS) - critter.wander = FALSE - critter.speak_chance = 0 - else if(body.stat == UNCONSCIOUS && prob(1)) - body.set_stat(CONSCIOUS) - critter.wander = TRUE - -/datum/mob_controller/passive/proc/set_flee_target(atom/A) - if(A) - flee_target = weakref(A) - turns_since_scan = 5 - /mob/living/simple_animal/passive possession_candidate = TRUE abstract_type = /mob/living/simple_animal/passive ai = /datum/mob_controller/passive - speak_chance = 0.5 - turns_per_wander = 5 see_in_dark = 6 minbodytemp = 223 maxbodytemp = 323 - -/mob/living/simple_animal/passive/attackby(var/obj/item/O, var/mob/user) - . = ..() - if(O.force) - 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/mob_controller/passive/preyi = ai - if(istype(preyi)) - preyi.set_flee_target(user) - -/mob/living/simple_animal/passive/explosion_act() - . = ..() - 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/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/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 b8903039385..9d49e0c4958 100644 --- a/code/modules/mob/living/simple_animal/passive/deer.dm +++ b/code/modules/mob/living/simple_animal/passive/deer.dm @@ -1,28 +1,32 @@ +/datum/mob_controller/passive/deer + emote_hear = list("bleats") + emote_see = list("shakes its head", "stamps a hoof", "looks around quickly") + emote_speech = list("Ough!", "Ourgh!", "Mroough!", "Broough?") + speak_chance = 0.25 + turns_per_wander = 10 + +/datum/mob_controller/passive/deer/buck + emote_hear = list("bellows") + /mob/living/simple_animal/passive/deer name = "deer" gender = NEUTER icon = 'icons/mob/simple_animal/doe.dmi' desc = "A fleet-footed forest animal known for its grace, speed and timidity." speak_emote = list("bleats") - emote_hear = list("bleats") - 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_wander = 5 see_in_dark = 6 faction = "deer" max_health = 60 butchery_data = /decl/butchery_data/animal/ruminant/deer natural_weapon = /obj/item/natural_weapon/hooves mob_size = MOB_SIZE_LARGE + ai = /datum/mob_controller/passive/deer /mob/living/simple_animal/passive/deer/doe name = "doe" icon = 'icons/mob/simple_animal/doe.dmi' butchery_data = /decl/butchery_data/animal/ruminant/deer gender = FEMALE - speak_emote = list("bleats") - emote_hear = list("bleats") /mob/living/simple_animal/passive/deer/buck name = "buck" @@ -30,22 +34,22 @@ butchery_data = /decl/butchery_data/animal/ruminant/deer/buck gender = MALE speak_emote = list("bellows") - emote_hear = list("bellows") + ai = /datum/mob_controller/passive/deer/buck /mob/living/simple_animal/passive/deer/Initialize() - if(gender == NEUTER) - if(prob(10)) // Internet seems to think a 10:1 ratio of bucks to does isn't uncommon, adjust later if this is bollocks + if(prob(10)) // Internet seems to think a 10:1 ratio of does to bucks isn't uncommon, adjust later if this is bollocks name = "buck" icon = 'icons/mob/simple_animal/buck.dmi' butchery_data = /decl/butchery_data/animal/ruminant/deer/buck gender = MALE speak_emote = list("bellows") - emote_hear = list("bellows") + ai = /datum/mob_controller/passive/deer/buck else name = "doe" icon = 'icons/mob/simple_animal/doe.dmi' butchery_data = /decl/butchery_data/animal/ruminant/deer gender = FEMALE - + speak_emote = list("bleats") + ai = /datum/mob_controller/passive/deer return ..() diff --git a/code/modules/mob/living/simple_animal/passive/fox.dm b/code/modules/mob/living/simple_animal/passive/fox.dm index b1ad39d92ca..0dd3c4dc6bc 100644 --- a/code/modules/mob/living/simple_animal/passive/fox.dm +++ b/code/modules/mob/living/simple_animal/passive/fox.dm @@ -1,81 +1,15 @@ -/datum/mob_controller/passive/fox - var/weakref/hunt_target - var/next_hunt = 0 - -/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) - for(var/mob/living/snack in view(body)) //search for a new target - if(can_hunt(snack)) - hunt_target = weakref(snack) - break - - return . || !!hunt_target - -/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/mob_controller/passive/fox/do_process(time_elapsed) - - ..() - - var/mob/living/simple_animal/critter = body - if(!istype(critter) || body.incapacitated() || body.current_posture?.prone || body.buckled || flee_target || !hunt_target) - return - - 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_wandering = FALSE - return - - // Find or pursue the target. - if(!body.Adjacent(hunt_target_atom)) - critter.set_moving_quickly() - critter.stop_wandering = TRUE - body.start_automove(hunt_target_atom) - return - - // Hunt/consume the target. - var/mob/living/hunt_mob = hunt_target_atom - if(hunt_mob.stat != DEAD) - critter.attack_target(hunt_target_atom) - - if(QDELETED(hunt_mob)) - hunt_target = null - critter.set_moving_slowly() - critter.stop_wandering = FALSE - return - - if(hunt_mob.stat != DEAD) - return - - // Eat the mob. - hunt_target = null - 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) - var/obj/item/remains/remains = new remains_type(get_turf(hunt_mob)) - remains.desc += "These look like they belonged to \a [hunt_mob.name]." - body.adjust_nutrition(5 * hunt_mob.get_max_health()) - next_hunt = world.time + rand(15 MINUTES, 30 MINUTES) - if(prob(5)) - hunt_mob.gib() - else - qdel(hunt_mob) - /mob/living/simple_animal/passive/fox name = "fox" 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/mob_controller/passive/fox + ai = /datum/mob_controller/passive/hunter/fox mob_size = MOB_SIZE_SMALL - emote_speech = list("Yip!","AIEE!","YIPE!") speak_emote = list("yelps", "yips", "hisses", "screams") - emote_hear = list("screams","yips") - emote_see = list("paces back and forth", "flicks its tail") pass_flags = PASS_FLAG_TABLE butchery_data = /decl/butchery_data/animal/fox + +/datum/mob_controller/passive/hunter/fox + emote_speech = list("Yip!","AIEE!","YIPE!") + emote_hear = list("screams","yips") + emote_see = list("paces back and forth", "flicks its tail") diff --git a/code/modules/mob/living/simple_animal/passive/mouse.dm b/code/modules/mob/living/simple_animal/passive/mouse.dm index 4fb3c20b809..fd0c345f708 100644 --- a/code/modules/mob/living/simple_animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_animal/passive/mouse.dm @@ -4,12 +4,7 @@ desc = "It's a small rodent." icon = 'icons/mob/simple_animal/mouse_gray.dmi' speak_emote = list("squeeks","squeeks","squiks") - emote_speech = list("Squeek!","SQUEEK!","Squeek?") - emote_hear = list("squeeks","squeaks","squiks") - emote_see = list("runs in a circle", "shakes", "scritches at something") pass_flags = PASS_FLAG_TABLE - speak_chance = 0.5 - turns_per_wander = 5 see_in_dark = 6 max_health = 5 response_harm = "stamps on" @@ -20,7 +15,6 @@ universal_understand = TRUE holder_type = /obj/item/holder mob_size = MOB_SIZE_MINISCULE - can_escape = TRUE can_pull_size = ITEM_SIZE_TINY can_pull_mobs = MOB_PULL_NONE base_animal_type = /mob/living/simple_animal/passive/mouse @@ -31,22 +25,27 @@ var/body_color //brown, gray and white, leave blank for random var/splatted = FALSE +/datum/mob_controller/passive/mouse + expected_type = /mob/living/simple_animal/passive/mouse + emote_speech = list("Squeek!","SQUEEK!","Squeek?") + emote_hear = list("squeeks","squeaks","squiks") + emote_see = list("runs in a circle", "shakes", "scritches at something") + speak_chance = 0.25 + turns_per_wander = 10 + can_escape_buckles = TRUE + /mob/living/simple_animal/passive/mouse/get_remains_type() return /obj/item/remains/mouse /mob/living/simple_animal/passive/mouse/get_dexterity(var/silent) return DEXTERITY_NONE // Mice are troll bait, give them no power. -/datum/mob_controller/passive/mouse - expected_type = /mob/living/simple_animal/passive/mouse - /datum/mob_controller/passive/mouse/do_process() ..() - var/mob/living/simple_animal/passive/mouse/mouse = body - if(prob(mouse.speak_chance)) - playsound(mouse.loc, 'sound/effects/mousesqueek.ogg', 50) - if(mouse.stat == UNCONSCIOUS && prob(5)) - INVOKE_ASYNC(mouse, TYPE_PROC_REF(/mob/living/simple_animal, audible_emote), "snuffles.") + if(prob(speak_chance)) + playsound(body.loc, 'sound/effects/mousesqueek.ogg', 50) + if(body.stat == UNCONSCIOUS && prob(5)) + INVOKE_ASYNC(body, TYPE_PROC_REF(/mob/living/simple_animal, audible_emote), "snuffles.") /mob/living/simple_animal/passive/mouse/Initialize() verbs += /mob/living/proc/ventcrawl diff --git a/code/modules/mob/living/simple_animal/passive/rabbit.dm b/code/modules/mob/living/simple_animal/passive/rabbit.dm index 6feaf09fd3b..73e3e9248d0 100644 --- a/code/modules/mob/living/simple_animal/passive/rabbit.dm +++ b/code/modules/mob/living/simple_animal/passive/rabbit.dm @@ -5,12 +5,15 @@ max_health = 20 natural_weapon = /obj/item/natural_weapon/bite/weak speak_emote = list("chitters") - emote_hear = list("chitters") - emote_see = list("hops","lifts its head","sniffs the air","wiggles its tail") mob_size = MOB_SIZE_TINY butchery_data = /decl/butchery_data/animal/rabbit - speak_chance = 0.5 holder_type = /obj/item/holder + ai = /datum/mob_controller/passive/rabbit + +/datum/mob_controller/passive/rabbit + emote_hear = list("chitters") + emote_see = list("hops","lifts its head","sniffs the air","wiggles its tail") + speak_chance = 0.25 /mob/living/simple_animal/passive/rabbit/brown name = "brown rabbit" diff --git a/code/modules/mob/living/simple_animal/simple_animal_combat.dm b/code/modules/mob/living/simple_animal/simple_animal_combat.dm deleted file mode 100644 index 7a74cc87226..00000000000 --- a/code/modules/mob/living/simple_animal/simple_animal_combat.dm +++ /dev/null @@ -1,17 +0,0 @@ -/mob/living/simple_animal/proc/attack_target(mob/target) - - if(buckled_mob == target && (!faction || buckled_mob.faction != faction)) - visible_message(SPAN_DANGER("\The [src] attempts to unseat \the [buckled_mob]!")) - set_dir(pick(global.cardinal)) - setClickCooldown(attack_delay) - if(prob(33)) - unbuckle_mob() - if(buckled_mob != target && !QDELETED(target)) - to_chat(target, SPAN_DANGER("You are thrown off \the [src]!")) - SET_STATUS_MAX(target, STAT_WEAK, 3) - return target - - if(isliving(target) && Adjacent(target)) - a_intent = I_HURT - UnarmedAttack(target, TRUE) - return target diff --git a/code/modules/mob/living/simple_animal/simple_animal_damage.dm b/code/modules/mob/living/simple_animal/simple_animal_damage.dm index ff4d978ae9e..639d9506852 100644 --- a/code/modules/mob/living/simple_animal/simple_animal_damage.dm +++ b/code/modules/mob/living/simple_animal/simple_animal_damage.dm @@ -26,19 +26,48 @@ if(gene_damage >= current_max_health) death() +/mob/living/simple_animal/get_life_damage_types() + var/static/list/life_damage_types = list( + BURN, + BRUTE, + CLONE + ) + return life_damage_types + /mob/living/simple_animal/adjustBruteLoss(var/amount, var/do_update_health = TRUE) brute_damage = clamp(brute_damage + amount, 0, get_max_health()) - return ..() + . = ..() /mob/living/simple_animal/adjustFireLoss(var/amount, var/do_update_health = TRUE) burn_damage = clamp(burn_damage + amount, 0, get_max_health()) if(do_update_health) update_health() + if(amount > 0 && istype(ai)) + ai.retaliate() -/mob/living/simple_animal/get_life_damage_types() - var/static/list/life_damage_types = list( - BURN, - BRUTE, - CLONE - ) - return life_damage_types +/mob/living/simple_animal/hit_with_weapon(obj/item/O, mob/living/user, var/effective_force, var/hit_zone) + + visible_message(SPAN_DANGER("\The [src] has been attacked with \the [O] by \the [user]!")) + if(istype(ai)) + ai.retaliate(user) + + if(O.force <= resistance) + to_chat(user, SPAN_WARNING("This weapon is ineffective; it does no damage.")) + return 0 + + var/damage = O.force + if (O.atom_damage_type == PAIN) + damage = 0 + if (O.atom_damage_type == STUN) + damage = (O.force / 8) + if(supernatural && istype(O,/obj/item/nullrod)) + damage *= 2 + purge = 3 + take_damage(damage, O.atom_damage_type, O.damage_flags()) + + return 1 + +/mob/living/simple_animal/take_damage(damage, damage_type = BRUTE, damage_flags, inflicter, armor_pen = 0, silent, do_update_health) + . = ..() + if((damage_type == BRUTE) && (damage_flags & (DAM_EDGE | DAM_SHARP | DAM_BULLET))) // damage flags that should cause bleeding + adjustBleedTicks(damage) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 81b99458d48..e39a54f6de9 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -476,7 +476,7 @@ var/global/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HURT) if(. == SAFE_PERP) return SAFE_PERP - if(!istype(src, /mob/living/simple_animal/hostile/retaliate/goat)) + if(!istype(src, /mob/living/simple_animal/hostile/goat)) threatcount += 4 return threatcount diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm index f6f86ce4f6e..fd6c30c5dd2 100644 --- a/code/modules/mob/mob_transformation_simple.dm +++ b/code/modules/mob/mob_transformation_simple.dm @@ -7,14 +7,14 @@ var/global/list/href_to_mob_type = list( "Robot" = /mob/living/silicon/robot ), "Animals" = list( - "Cat" = /mob/living/simple_animal/cat, - "Runtime" = /mob/living/simple_animal/cat/fluff/runtime, + "Cat" = /mob/living/simple_animal/passive/cat, + "Runtime" = /mob/living/simple_animal/passive/cat/fluff/runtime, "Corgi" = /mob/living/simple_animal/corgi, "Ian" = /mob/living/simple_animal/corgi/Ian, "Crab" = /mob/living/simple_animal/crab, "Coffee" = /mob/living/simple_animal/crab/Coffee, - "Parrot" = /mob/living/simple_animal/hostile/retaliate/parrot, - "Poly" = /mob/living/simple_animal/hostile/retaliate/parrot/Poly, + "Parrot" = /mob/living/simple_animal/hostile/parrot, + "Poly" = /mob/living/simple_animal/hostile/parrot/Poly, ) ) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 7522407c8ae..f97b47859a2 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -241,7 +241,7 @@ INITIALIZE_IMMEDIATE(/mob/new_player) else if(spawnpoint.spawn_announcement) AnnounceCyborg(character, job, spawnpoint.spawn_announcement) - callHook("player_latejoin", list(job, character)) + RAISE_EVENT(/decl/observ/player_latejoin, character, job) log_and_message_admins("has joined the round as [character.mind.assigned_role].", character) qdel(src) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 1b5ca140335..49db20116bf 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -135,7 +135,7 @@ O.dropInto(loc) O.job = ASSIGNMENT_ROBOT - callHook("borgify", list(O)) + RAISE_EVENT(/decl/observ/cyborg_created, O) O.Namepick() qdel(src) // Queues us for a hard delete @@ -223,7 +223,7 @@ return 0 //Sanity, this should never happen. //Good mobs! - if(ispath(MP, /mob/living/simple_animal/cat)) + if(ispath(MP, /mob/living/simple_animal/passive/cat)) return 1 if(ispath(MP, /mob/living/simple_animal/corgi)) return 1 @@ -239,7 +239,7 @@ return 1 if(ispath(MP, /mob/living/simple_animal/hostile/bear)) return 1 - if(ispath(MP, /mob/living/simple_animal/hostile/retaliate/parrot)) + if(ispath(MP, /mob/living/simple_animal/hostile/parrot)) return 1 //Not in here? Must be untested! diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index c21cadae0bf..68257d94e53 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -198,7 +198,7 @@ if(computer) id_card.assignment = "Terminated" remove_nt_access(id_card) - callHook("terminate_employee", list(id_card)) + RAISE_EVENT(/decl/observ/employee_id_terminated, id_card) if("edit") if(!(get_file_perms(module.get_access(user), user) & OS_WRITE_ACCESS)) to_chat(usr, SPAN_WARNING("Access denied.")) @@ -296,7 +296,7 @@ id_card.assignment = t1 id_card.position = t1 - callHook("reassign_employee", list(id_card)) + RAISE_EVENT(/decl/observ/employee_id_reassigned, id_card) if("access") if(href_list["allowed"] && id_card) var/access_type = href_list["access_target"] diff --git a/code/modules/modular_computers/file_system/reports/crew_record.dm b/code/modules/modular_computers/file_system/reports/crew_record.dm index 6364d2c4bca..8138483f2ad 100644 --- a/code/modules/modular_computers/file_system/reports/crew_record.dm +++ b/code/modules/modular_computers/file_system/reports/crew_record.dm @@ -23,11 +23,11 @@ var/global/arrest_security_status = "Arrest" . = ..() global.all_crew_records.Remove(src) -/datum/computer_file/report/crew_record/proc/load_from_mob(var/mob/living/human/H) +/datum/computer_file/report/crew_record/proc/load_from_mob(var/mob/living/human/crewmember) - if(istype(H)) - photo_front = getFlatIcon(H, SOUTH, always_use_defdir = 1) - photo_side = getFlatIcon(H, WEST, always_use_defdir = 1) + if(istype(crewmember)) + photo_front = getFlatIcon(crewmember, SOUTH, always_use_defdir = 1) + photo_side = getFlatIcon(crewmember, WEST, always_use_defdir = 1) else var/mob/living/human/dummy = new() photo_front = getFlatIcon(dummy, SOUTH, always_use_defdir = 1) @@ -35,73 +35,73 @@ var/global/arrest_security_status = "Arrest" qdel(dummy) // Load records from the client. - var/list/records = H?.client?.prefs?.records || list() + var/list/records = crewmember?.client?.prefs?.records || list() // Add honorifics, etc. var/formal_name = "Unset" - if(H) - formal_name = H.real_name - if(H.client && H.client.prefs) - for(var/culturetag in H.client.prefs.cultural_info) - var/decl/cultural_info/culture = GET_DECL(H.client.prefs.cultural_info[culturetag]) - if(H.char_rank && H.char_rank.name_short) + if(crewmember) + formal_name = crewmember.real_name + if(crewmember.client && crewmember.client.prefs) + for(var/culturetag in crewmember.client.prefs.cultural_info) + var/decl/cultural_info/culture = GET_DECL(crewmember.client.prefs.cultural_info[culturetag]) + if(crewmember.char_rank && crewmember.char_rank.name_short) formal_name = "[formal_name][culture.get_formal_name_suffix()]" else formal_name = "[culture.get_formal_name_prefix()][formal_name][culture.get_formal_name_suffix()]" // Generic record - set_name(H ? H.real_name : "Unset") + set_name(crewmember ? crewmember.real_name : "Unset") set_formal_name(formal_name) - set_job(H ? GetAssignment(H) : "Unset") + set_job(crewmember ? GetAssignment(crewmember) : "Unset") var/gender_term = "Unset" - if(H) - var/decl/pronouns/G = H.get_pronouns(ignore_coverings = TRUE) - if(G && G.bureaucratic_term ) - gender_term = G.bureaucratic_term + if(crewmember) + var/decl/pronouns/gender = crewmember.get_pronouns(ignore_coverings = TRUE) + if(gender?.bureaucratic_term) + gender_term = gender.bureaucratic_term set_gender(gender_term) - set_age(H?.get_age() || 30) + set_age(crewmember?.get_age() || 30) set_status(global.default_physical_status) - set_species_name(H ? H.get_species_name() : global.using_map.default_species) - set_branch(H ? (H.char_branch && H.char_branch.name) : "None") - set_rank(H ? (H.char_rank && H.char_rank.name) : "None") + set_species_name(crewmember ? crewmember.get_species_name() : global.using_map.default_species) + set_branch(crewmember ? (crewmember.char_branch && crewmember.char_branch.name) : "None") + set_rank(crewmember ? (crewmember.char_rank && crewmember.char_rank.name) : "None") var/public_record = records[PREF_PUB_RECORD] - set_public_record((public_record && !jobban_isbanned(H, "Records")) ? html_decode(public_record) : "No record supplied") + set_public_record((public_record && !jobban_isbanned(crewmember, "Records")) ? html_decode(public_record) : "No record supplied") // Medical record - set_bloodtype(H?.get_blood_type() || "Unset") + set_bloodtype(crewmember?.get_blood_type() || "Unset") var/medical_record = records[PREF_MED_RECORD] - set_medical_record((medical_record && !jobban_isbanned(H, "Records")) ? html_decode(medical_record) : "No record supplied") + set_medical_record((medical_record && !jobban_isbanned(crewmember, "Records")) ? html_decode(medical_record) : "No record supplied") - if(H) - var/decl/bodytype/root_bodytype = H.get_bodytype() + if(crewmember) + var/decl/bodytype/root_bodytype = crewmember.get_bodytype() var/organ_data = list("\[*\]") - for(var/obj/item/organ/external/E in H.get_external_organs()) - if(E.bodytype != root_bodytype) - organ_data += "[E.bodytype] [E.name] [BP_IS_PROSTHETIC(E) ? "prosthetic" : "graft"]" - for(var/obj/item/organ/internal/I in H.get_internal_organs()) - if(I.bodytype != root_bodytype) - organ_data += "[I.bodytype] [I.name] [BP_IS_PROSTHETIC(I) ? "prosthetic" : "graft"]" + for(var/obj/item/organ/external/child in crewmember.get_external_organs()) + if(child.bodytype != root_bodytype) + organ_data += "[child.bodytype] [child.name] [BP_IS_PROSTHETIC(child) ? "prosthetic" : "graft"]" + for(var/obj/item/organ/internal/internal_organ in crewmember.get_internal_organs()) + if(internal_organ.bodytype != root_bodytype) + organ_data += "[internal_organ.bodytype] [internal_organ.name] [BP_IS_PROSTHETIC(internal_organ) ? "prosthetic" : "graft"]" set_implants(jointext(organ_data, "\[*\]")) // Security record set_criminalStatus(global.default_security_status) - set_dna(H?.get_unique_enzymes() || "") - set_fingerprint(H?.get_full_print(ignore_blockers = TRUE) || "") + set_dna(crewmember?.get_unique_enzymes() || "") + set_fingerprint(crewmember?.get_full_print(ignore_blockers = TRUE) || "") var/security_record = records[PREF_SEC_RECORD] - set_security_record((security_record && !jobban_isbanned(H, "Records")) ? html_decode(security_record) : "No record supplied") + set_security_record((security_record && !jobban_isbanned(crewmember, "Records")) ? html_decode(security_record) : "No record supplied") // Employment record var/employment_record = "No record supplied" - if(H) + if(crewmember) var/gen_record = records[PREF_GEN_RECORD] - if(gen_record && !jobban_isbanned(H, "Records")) + if(gen_record && !jobban_isbanned(crewmember, "Records")) employment_record = html_decode(gen_record) - if(H.client && H.client.prefs) + if(crewmember.client && crewmember.client.prefs) var/list/qualifications - for(var/culturetag in H.client.prefs.cultural_info) - var/decl/cultural_info/culture = GET_DECL(H.client.prefs.cultural_info[culturetag]) + for(var/culturetag in crewmember.client.prefs.cultural_info) + var/decl/cultural_info/culture = GET_DECL(crewmember.client.prefs.cultural_info[culturetag]) var/extra_note = culture.get_qualifications() if(extra_note) LAZYADD(qualifications, extra_note) @@ -110,46 +110,46 @@ var/global/arrest_security_status = "Arrest" set_employment_record(employment_record) // Misc cultural info. - set_residence(H ? html_decode(H.get_cultural_value(TAG_HOMEWORLD)) : "Unset") - set_faction(H ? html_decode(H.get_cultural_value(TAG_FACTION)) : "Unset") - set_religion(H ? html_decode(H.get_cultural_value(TAG_RELIGION)) : "Unset") + set_residence(crewmember ? html_decode(crewmember.get_cultural_value(TAG_HOMEWORLD)) : "Unset") + set_faction(crewmember ? html_decode(crewmember.get_cultural_value(TAG_FACTION)) : "Unset") + set_religion(crewmember ? html_decode(crewmember.get_cultural_value(TAG_RELIGION)) : "Unset") - if(H) + if(crewmember) var/skills = list() - for(var/decl/hierarchy/skill/S in global.using_map.get_available_skills()) - var/level = H.get_skill_value(S.type) + for(var/decl/hierarchy/skill/skill in global.using_map.get_available_skills()) + var/level = crewmember.get_skill_value(skill.type) if(level > SKILL_NONE) - skills += "[S.name], [S.levels[level]]" + skills += "[skill.name], [skill.levels[level]]" set_skillset(jointext(skills,"\n")) // Antag record - if(H?.client?.prefs) - var/exploit_record = H.client.prefs.exploit_record - set_antag_record((exploit_record && !jobban_isbanned(H, "Records")) ? html_decode(exploit_record) : "") + if(crewmember?.client?.prefs) + var/exploit_record = crewmember.client.prefs.exploit_record + set_antag_record((exploit_record && !jobban_isbanned(crewmember, "Records")) ? html_decode(exploit_record) : "") // Cut down version for silicons -/datum/computer_file/report/crew_record/synth/load_from_mob(var/mob/living/silicon/S) - if(istype(S)) - photo_front = getFlatIcon(S, SOUTH, always_use_defdir = 1) - photo_side = getFlatIcon(S, WEST, always_use_defdir = 1) +/datum/computer_file/report/crew_record/synth/load_from_mob(var/mob/living/silicon/silicon_crewmember) + if(istype(silicon_crewmember)) + photo_front = getFlatIcon(silicon_crewmember, SOUTH, always_use_defdir = 1) + photo_side = getFlatIcon(silicon_crewmember, WEST, always_use_defdir = 1) // Generic record - set_name(S ? S.real_name : "Unset") - set_formal_name(S ? S.real_name : "Unset") + set_name(silicon_crewmember ? silicon_crewmember.real_name : "Unset") + set_formal_name(silicon_crewmember ? silicon_crewmember.real_name : "Unset") set_gender("Unset") set_status(global.default_physical_status) var/silicon_type = "Synthetic Lifeform" - var/robojob = GetAssignment(S) - if(isrobot(S)) - var/mob/living/silicon/robot/R = S - silicon_type = R.braintype - if(R.module) - robojob = "[R.module.display_name] [silicon_type]" - if(isAI(S)) + var/robojob = GetAssignment(silicon_crewmember) + if(isrobot(silicon_crewmember)) + var/mob/living/silicon/robot/robot = silicon_crewmember + silicon_type = robot.braintype + if(robot.module) + robojob = "[robot.module.display_name] [silicon_type]" + if(isAI(silicon_crewmember)) silicon_type = "AI" robojob = "Artificial Intelligence" - set_job(S ? robojob : "Unset") + set_job(silicon_crewmember ? robojob : "Unset") set_species_name(silicon_type) set_implants("Robotic body") @@ -159,57 +159,57 @@ var/global/arrest_security_status = "Arrest" // Global methods // Used by character creation to create a record for new arrivals. -/proc/CreateModularRecord(var/mob/living/H, record_type = /datum/computer_file/report/crew_record) - var/datum/computer_file/report/crew_record/CR = new record_type() - global.all_crew_records.Add(CR) - CR.load_from_mob(H) - var/datum/computer_network/network = get_local_network_at(get_turf(H)) +/proc/CreateModularRecord(var/mob/living/crewmember, record_type = /datum/computer_file/report/crew_record) + var/datum/computer_file/report/crew_record/crew_record = new record_type() + global.all_crew_records.Add(crew_record) + crew_record.load_from_mob(crewmember) + var/datum/computer_network/network = get_local_network_at(get_turf(crewmember)) if(network) - network.store_file(CR, OS_RECORDS_DIR, TRUE, mainframe_role = MF_ROLE_CREW_RECORDS) - return CR + network.store_file(crew_record, OS_RECORDS_DIR, TRUE, mainframe_role = MF_ROLE_CREW_RECORDS) + return crew_record // Gets crew records filtered by set of positions /proc/department_crew_manifest(var/list/filter_positions, var/blacklist = FALSE) var/list/matches = list() - for(var/datum/computer_file/report/crew_record/CR in global.all_crew_records) - var/rank = CR.get_job() + for(var/datum/computer_file/report/crew_record/crew_record in global.all_crew_records) + var/rank = crew_record.get_job() if(blacklist) if(!(rank in filter_positions)) - matches.Add(CR) + matches.Add(crew_record) else if(rank in filter_positions) - matches.Add(CR) + matches.Add(crew_record) return matches // Simple record to HTML (for paper purposes) conversion. // Not visually that nice, but it gets the work done, feel free to tweak it visually -/proc/record_to_html(var/datum/computer_file/report/crew_record/CR, var/access) +/proc/record_to_html(var/datum/computer_file/report/crew_record/crew_record, var/access) var/dat = "

RECORD DATABASE DATA DUMP

Generated on: [stationdate2text()] [stationtime2text()]
******************************
" dat += "" - for(var/datum/report_field/F in CR.fields) - if(F.get_perms(access) & OS_READ_ACCESS) - dat += "" - dat += "
[F.display_name()]" - if(F.needs_big_box) + for(var/datum/report_field/field in crew_record.fields) + if(field.get_perms(access) & OS_READ_ACCESS) + dat += "
[field.display_name()]" + if(field.needs_big_box) dat += "
[F.get_value()]" + dat += "[field.get_value()]" dat += "" return dat //Should only be used for OOC stuff, for player-facing stuff you must go through the network. /proc/get_crewmember_record(var/name) - for(var/datum/computer_file/report/crew_record/CR in global.all_crew_records) - if(CR.get_name() == name) - return CR + for(var/datum/computer_file/report/crew_record/crew_record in global.all_crew_records) + if(crew_record.get_name() == name) + return crew_record return null -/proc/GetAssignment(var/mob/living/human/H) - if(!H) +/proc/GetAssignment(var/mob/living/crewmember) + if(!crewmember) return "Unassigned" - if(!H.mind) - return H.job - if(H.mind.role_alt_title) - return H.mind.role_alt_title - return H.mind.assigned_role + if(!crewmember.mind) + return crewmember.job + if(crewmember.mind.role_alt_title) + return crewmember.mind.role_alt_title + return crewmember.mind.assigned_role #define GETTER_SETTER(PATH, KEY) /datum/computer_file/report/crew_record/proc/get_##KEY(){var/datum/report_field/F = locate(/datum/report_field/##PATH/##KEY) in fields; if(F) return F.get_value()} \ /datum/computer_file/report/crew_record/proc/set_##KEY(given_value){var/datum/report_field/F = locate(/datum/report_field/##PATH/##KEY) in fields; if(F) F.set_value(given_value)} @@ -279,16 +279,16 @@ FIELD_LONG("Exploitable Information", antag_record, access_hacked, access_hacked . |= "Unset" var/list/all_genders = decls_repository.get_decls_of_type(/decl/pronouns) for(var/thing in all_genders) - var/decl/pronouns/G = all_genders[thing] - if(G.bureaucratic_term ) - . |= G.bureaucratic_term + var/decl/pronouns/gender = all_genders[thing] + if(gender.bureaucratic_term ) + . |= gender.bureaucratic_term /datum/report_field/options/crew_record/branch/proc/record_branches() . = list() . |= "Unset" - for(var/B in mil_branches.branches) - var/datum/mil_branch/BR = mil_branches.branches[B] - . |= BR.name + for(var/branch in mil_branches.branches) + var/datum/mil_branch/branch_datum = mil_branches.branches[branch] + . |= branch_datum.name #undef GETTER_SETTER #undef SETUP_FIELD diff --git a/code/modules/multiz/hoist.dm b/code/modules/multiz/hoist.dm index 02c3988c4a6..6a3c80e71fe 100644 --- a/code/modules/multiz/hoist.dm +++ b/code/modules/multiz/hoist.dm @@ -48,15 +48,14 @@ /obj/effect/hoist_hook/receive_mouse_drop(atom/dropping, mob/user, params) // skip the parent buckle logic, handle climbing directly - var/mob/living/H = user - if(istype(H) && !H.anchored && can_climb(H) && dropping == user) + if(isliving(user) && !user.anchored && can_climb(user) && dropping == user) do_climb(dropping) return TRUE // end copypasta'd code if(istype(dropping, /atom/movable)) - var/atom/movable/AM = dropping - if(!AM.simulated || AM.anchored) - to_chat(user, SPAN_WARNING("You can't do that with \the [AM].")) + var/atom/movable/dropped_movable = dropping + if(!dropped_movable.simulated || dropped_movable.anchored) + to_chat(user, SPAN_WARNING("You can't do that with \the [dropped_movable].")) return TRUE if(source_hoist.hoistee) to_chat(user, SPAN_NOTICE("\The [source_hoist.hoistee] is already attached to \the [src]!")) @@ -66,24 +65,24 @@ return if (!user.check_dexterity(DEXTERITY_HOLD_ITEM)) return - source_hoist.attach_hoistee(AM) + source_hoist.attach_hoistee(dropped_movable) user.visible_message( - SPAN_NOTICE("[user] attaches \the [AM] to \the [src]."), - SPAN_NOTICE("You attach \the [AM] to \the [src]."), + SPAN_NOTICE("[user] attaches \the [dropped_movable] to \the [src]."), + SPAN_NOTICE("You attach \the [dropped_movable] to \the [src]."), "You hear something clamp into place.") return TRUE -/obj/structure/hoist/proc/attach_hoistee(atom/movable/AM) - hoistee = AM - if(ismob(AM)) - source_hook.buckle_mob(AM) +/obj/structure/hoist/proc/attach_hoistee(atom/movable/victim) + hoistee = victim + if(ismob(victim)) + source_hook.buckle_mob(victim) else - AM.anchored = TRUE // can't buckle non-mobs at the moment - source_hook.layer = AM.layer + 0.1 - if (get_turf(AM) != get_turf(source_hook)) - AM.forceMove(get_turf(source_hook)) + victim.anchored = TRUE // can't buckle non-mobs at the moment + source_hook.layer = victim.layer + 0.1 + if (get_turf(victim) != get_turf(source_hook)) + victim.forceMove(get_turf(source_hook)) - events_repository.register(/decl/observ/destroyed, AM, src, PROC_REF(release_hoistee)) + events_repository.register(/decl/observ/destroyed, victim, src, PROC_REF(release_hoistee)) /obj/effect/hoist_hook/handle_mouse_drop(atom/over, mob/user, params) if(source_hoist.hoistee && isturf(over) && over.Adjacent(source_hoist.hoistee)) @@ -105,9 +104,9 @@ /obj/effect/hoist_hook/unbuckle_mob() . = ..() if (. && !QDELETED(source_hoist)) - var/mob/M = . + var/mob/victim = . source_hoist.hoistee = null - M.fall(get_turf(src)) // fuck you, you fall now! + victim.fall(get_turf(src)) // fuck you, you fall now! /obj/structure/hoist name = "hoist" @@ -210,19 +209,11 @@ check_consistency() - var/size - if (ismob(hoistee)) - var/mob/M = hoistee - size = M.mob_size - else if (isobj(hoistee)) - var/obj/O = hoistee - size = O.w_class - user.visible_message( SPAN_NOTICE("[user] begins to [movtext] \the [hoistee]!"), SPAN_NOTICE("You begin to [movtext] \the [hoistee]!"), SPAN_NOTICE("You hear the sound of a crank.")) - if (do_after(user, (1 SECONDS) * size / 4, src)) + if (do_after(user, (1 SECONDS) * get_object_size(hoistee) / 4, src)) move_dir(movedir, 1) return TRUE @@ -265,8 +256,8 @@ if (source_hook in get_step(src, dir)) // you don't get to move above the hoist return FALSE if (DOWN) - var/turf/T = get_turf(source_hook) - if(!istype(T) || !T.is_open()) // can't move down through a solid tile + var/turf/current_turf = get_turf(source_hook) + if(!current_turf || !current_turf.is_open()) // can't move down through a solid tile or if not on a turf return FALSE return TRUE // i thought i could trust myself to write something as simple as this, guess i was wrong diff --git a/code/modules/multiz/level_data.dm b/code/modules/multiz/level_data.dm index 343808267b8..80e5b9cd74e 100644 --- a/code/modules/multiz/level_data.dm +++ b/code/modules/multiz/level_data.dm @@ -585,6 +585,8 @@ INITIALIZE_IMMEDIATE(/obj/abstract/level_data_spawner) // Level Data Implementations //////////////////////////////////////////// /datum/level_data/space + daycycle_id = "space_solars" + daycycle_type = /datum/daycycle/solars /datum/level_data/debug name = "Debug Level" diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index 5856f901e69..7e79f121407 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -85,8 +85,8 @@ if((limb_flags & ORGAN_FLAG_FINGERPRINT) && !BP_IS_PROSTHETIC(src)) fingerprint = value else - for(var/obj/item/organ/external/E in children) - E.set_fingerprint(value) + for(var/obj/item/organ/external/child in children) + child.set_fingerprint(value) /obj/item/organ/external/proc/get_fingerprint() @@ -95,19 +95,19 @@ fingerprint = md5(sequential_id(/mob)) return fingerprint - for(var/obj/item/organ/external/E in children) - var/print = E.get_fingerprint() + for(var/obj/item/organ/external/child in children) + var/print = child.get_fingerprint() if(print) return print -/obj/item/organ/external/afterattack(atom/A, mob/user, proximity) +/obj/item/organ/external/afterattack(atom/target, mob/user, proximity) ..() if(proximity && get_fingerprint()) var/datum/extension/forensic_evidence/forensics = get_or_create_extension(src, /datum/extension/forensic_evidence) - var/datum/fingerprint/F = new() - F.full_print = get_fingerprint() - F.completeness = rand(10,90) - forensics.add_data(/datum/forensics/fingerprints, F) + var/datum/fingerprint/fingerprint = new() + fingerprint.full_print = get_fingerprint() + fingerprint.completeness = rand(10,90) + forensics.add_data(/datum/forensics/fingerprints, fingerprint) /obj/item/organ/external/Initialize(mapload, material_key, datum/mob_snapshot/supplied_appearance, decl/bodytype/new_bodytype) . = ..() @@ -197,9 +197,9 @@ burn_damage = 7.5 var/power = 4 - severity //stupid reverse severity - for(var/obj/item/I in implants) - if(I.obj_flags & OBJ_FLAG_CONDUCTIBLE) - burn_damage += I.w_class * rand(power, 3*power) + for(var/obj/item/implanted_item in implants) + if(implanted_item.obj_flags & OBJ_FLAG_CONDUCTIBLE) + burn_damage += implanted_item.w_class * rand(power, 3*power) if(owner && burn_damage) owner.custom_pain("Something inside your [src] burns a [severity < 2 ? "bit" : "lot"]!", power * 15) //robotic organs won't feel it anyway @@ -213,32 +213,30 @@ if((owner && loc == owner) || !contents.len) return ..() var/list/removable_objects = list() - for(var/obj/item/organ/external/E in (contents + src)) - if(!istype(E)) - continue - for(var/obj/item/I in E.contents) - if(istype(I,/obj/item/organ)) + for(var/obj/item/organ/external/child_organ in (contents + src)) + for(var/obj/item/embedded in child_organ.contents) + if(istype(embedded, /obj/item/organ)) continue - removable_objects |= I + removable_objects |= embedded if(removable_objects.len) - var/obj/item/I = pick(removable_objects) - I.forceMove(get_turf(user)) //just in case something was embedded that is not an item - if(istype(I) && user.get_empty_hand_slot()) - user.put_in_hands(I) - user.visible_message(SPAN_DANGER("\The [user] rips \the [I] out of \the [src]!")) + var/obj/item/embedded = pick(removable_objects) + embedded.forceMove(get_turf(user)) //just in case something was embedded that is not an item + if(istype(embedded) && user.get_empty_hand_slot()) + user.put_in_hands(embedded) + user.visible_message(SPAN_DANGER("\The [user] rips \the [embedded] out of \the [src]!")) return //no eating the limb until everything's been removed return ..() /obj/item/organ/external/examine(mob/user, distance) . = ..() if(distance <= 1 || isghost(user)) - for(var/obj/item/I in contents) - if(istype(I, /obj/item/organ)) + for(var/obj/item/embedded in contents) + if(istype(embedded, /obj/item/organ)) continue - to_chat(user, "There is \a [I] sticking out of it.") + to_chat(user, SPAN_DANGER("There is \a [embedded] sticking out of it.")) var/ouchies = get_wounds_desc() if(ouchies != "nothing") - to_chat(user, "There is [ouchies] visible on it.") + to_chat(user, SPAN_NOTICE("There is [ouchies] visible on it.")) return @@ -247,105 +245,105 @@ for(var/obj/item/organ/external/child in children) child.show_decay_status(user) -/obj/item/organ/external/attackby(obj/item/W, mob/user) +/obj/item/organ/external/attackby(obj/item/used_item, mob/user) - var/obj/item/organ/external/E = W - if(BP_IS_PROSTHETIC(src) && istype(E) && BP_IS_PROSTHETIC(E)) + var/obj/item/organ/external/connecting_limb = used_item + if(BP_IS_PROSTHETIC(src) && istype(connecting_limb) && BP_IS_PROSTHETIC(connecting_limb)) var/combined = FALSE - if(E.organ_tag == parent_organ) + if(connecting_limb.organ_tag == parent_organ) - if(length(E.children)) - to_chat(usr, SPAN_WARNING("You cannot connect additional limbs to \the [E].")) + if(length(connecting_limb.children)) + to_chat(usr, SPAN_WARNING("You cannot connect additional limbs to \the [connecting_limb].")) return - var/mob/M = loc - if(istype(M)) - M.try_unequip(src, E) + var/mob/holder = loc + if(istype(holder)) + holder.try_unequip(src, connecting_limb) else dropInto(loc) - forceMove(E) + forceMove(connecting_limb) - if(loc != E) + if(loc != connecting_limb) return - if(istype(E.owner)) - E.owner.add_organ(src, E) + if(istype(connecting_limb.owner)) + connecting_limb.owner.add_organ(src, connecting_limb) else - do_install(null, E) + do_install(null, connecting_limb) combined = TRUE - else if(E.parent_organ == organ_tag) + else if(connecting_limb.parent_organ == organ_tag) if(LAZYLEN(children)) to_chat(usr, SPAN_WARNING("You cannot connect additional limbs to \the [src].")) return - if(!user.try_unequip(E, src)) + if(!user.try_unequip(connecting_limb, src)) return - if(istype(E.owner)) - E.owner.add_organ(E, src) + if(istype(connecting_limb.owner)) + connecting_limb.owner.add_organ(connecting_limb, src) else - E.do_install(null, src) + connecting_limb.do_install(null, src) combined = TRUE else - to_chat(user, SPAN_WARNING("\The [E] cannot be connected to \the [src].")) + to_chat(user, SPAN_WARNING("\The [connecting_limb] cannot be connected to \the [src].")) return if(combined) - to_chat(user, SPAN_NOTICE("You connect \the [E] to \the [src].")) + to_chat(user, SPAN_NOTICE("You connect \the [connecting_limb] to \the [src].")) compile_icon() update_icon() - E.compile_icon() - E.update_icon() + connecting_limb.compile_icon() + connecting_limb.update_icon() return //Remove sub-limbs - if(W.get_tool_quality(TOOL_SAW) && LAZYLEN(children) && try_saw_off_child(W, user)) + if(used_item.get_tool_quality(TOOL_SAW) && LAZYLEN(children) && try_saw_off_child(used_item, user)) return //Remove internal items/organs/implants - if(try_remove_internal_item(W, user)) + if(try_remove_internal_item(used_item, user)) return ..() //Handles removing internal organs/implants/items still in the detached limb. -/obj/item/organ/external/proc/try_remove_internal_item(var/obj/item/W, var/mob/user) +/obj/item/organ/external/proc/try_remove_internal_item(var/obj/item/used_item, var/mob/user) - if(stage == 0 && W.sharp) - user.visible_message(SPAN_NOTICE("\The [user] cuts \the [src] open with \the [W].")) + if(stage == 0 && used_item.sharp) + user.visible_message(SPAN_NOTICE("\The [user] cuts \the [src] open with \the [used_item].")) stage++ return TRUE - if(stage == 1 && IS_RETRACTOR(W)) - user.visible_message(SPAN_NOTICE("\The [user] levers \the [src] open with \the [W].")) + if(stage == 1 && IS_RETRACTOR(used_item)) + user.visible_message(SPAN_NOTICE("\The [user] levers \the [src] open with \the [used_item].")) stage++ return TRUE - if(stage == 2 && (W.sharp || IS_HEMOSTAT(W) || IS_WIRECUTTER(W))) + if(stage == 2 && (used_item.sharp || IS_HEMOSTAT(used_item) || IS_WIRECUTTER(used_item))) var/list/radial_buttons = make_item_radial_menu_choices(get_contents_recursive()) if(LAZYLEN(radial_buttons)) var/obj/item/removing = show_radial_menu(user, src, radial_buttons, radius = 42, require_near = TRUE, use_labels = TRUE, check_locs = list(src)) if(removing) if(istype(removing, /obj/item/organ)) - var/obj/item/organ/O = removing - O.do_uninstall() + var/obj/item/organ/removed_organ = removing + removed_organ.do_uninstall() removing.forceMove(get_turf(user)) if(user.get_empty_hand_slot()) user.put_in_hands(removing) - user.visible_message(SPAN_NOTICE("\The [user] extracts [removing] from \the [src] with \the [W]!")) + user.visible_message(SPAN_NOTICE("\The [user] extracts [removing] from \the [src] with \the [used_item]!")) else - user.visible_message(SPAN_NOTICE("\The [user] fishes around fruitlessly in \the [src] with \the [W].")) + user.visible_message(SPAN_NOTICE("\The [user] fishes around fruitlessly in \the [src] with \the [used_item].")) return TRUE return FALSE //Handles removing child limbs from the detached limb. -/obj/item/organ/external/proc/try_saw_off_child(var/obj/item/W, var/mob/user) +/obj/item/organ/external/proc/try_saw_off_child(var/obj/item/used_item, var/mob/user) //Add icons to radial menu var/list/radial_buttons = make_item_radial_menu_choices(get_limbs_recursive()) @@ -357,7 +355,7 @@ if(!istype(removing)) return TRUE - var/cutting_result = !W.do_tool_interaction(TOOL_SAW, user, src, 3 SECONDS, "cutting \the [removing] off") + var/cutting_result = !used_item.do_tool_interaction(TOOL_SAW, user, src, 3 SECONDS, "cutting \the [removing] off") //Check if the limb is still in the hierarchy if(cutting_result == 1 || !(removing in get_limbs_recursive())) if(cutting_result != -1) @@ -373,7 +371,7 @@ removing.update_icon() if(user.get_empty_hand_slot()) user.put_in_hands(removing) - user.visible_message(SPAN_DANGER("[user] cuts off \the [removing] from [src] with [W]!")) + user.visible_message(SPAN_DANGER("[user] cuts off \the [removing] from [src] with [used_item]!")) return TRUE /** @@ -405,11 +403,11 @@ return (status & ORGAN_DISLOCATED) || is_parent_dislocated() //if any parent is dislocated, we are considered dislocated as well /obj/item/organ/external/proc/is_parent_dislocated() - var/obj/item/organ/external/O = parent - while(O && (O.limb_flags & ORGAN_FLAG_CAN_DISLOCATE)) - if(O.status & ORGAN_DISLOCATED) + var/obj/item/organ/external/current_limb = parent + while(current_limb && (current_limb.limb_flags & ORGAN_FLAG_CAN_DISLOCATE)) + if(current_limb.status & ORGAN_DISLOCATED) return TRUE - O = O.parent + current_limb = current_limb.parent return FALSE /obj/item/organ/external/proc/update_internal_organs_cost() @@ -475,15 +473,15 @@ // //Add any existing organs in the owner that have us as parent // - for(var/obj/item/organ/internal/I in owner.get_internal_organs()) - if(I.parent_organ == organ_tag) - LAZYDISTINCTADD(internal_organs, I) + for(var/obj/item/organ/internal/internal_organ in owner.get_internal_organs()) + if(internal_organ.parent_organ == organ_tag) + LAZYDISTINCTADD(internal_organs, internal_organ) update_internal_organs_cost() - for(var/obj/item/organ/external/E in owner.get_external_organs()) - if(E.parent_organ == organ_tag) - E.parent = src - LAZYDISTINCTADD(children, E) + for(var/obj/item/organ/external/external_organ in owner.get_external_organs()) + if(external_organ.parent_organ == organ_tag) + external_organ.parent = src + LAZYDISTINCTADD(children, external_organ) //Add any existing implants that should be refering us for(var/obj/implant in implants) @@ -496,10 +494,10 @@ //Since limbs attached during surgery have their internal organs detached, we want to re-attach them if we're doing the proper install of the parent limb else if(istype(implant, /obj/item/organ) && !detached) - var/obj/item/organ/O = implant - if(O.parent_organ == organ_tag) + var/obj/item/organ/detached_organ = implant + if(detached_organ.parent_organ == organ_tag) //The add_organ chain will automatically handle properly removing the detached flag, and moving it to the proper lists - owner.add_organ(O, src, in_place, update_icon, detached) + owner.add_organ(detached_organ, src, in_place, update_icon, detached) else //Handle installing into a stand-alone parent limb to keep dropped limbs in some kind of coherent state if(!affected) @@ -523,9 +521,9 @@ LAZYDISTINCTADD(parent.children, src) //Even when detached the limb has to be in the children list, because of the way limbs icon are handled //Remove any stump wound for this slot - for(var/datum/wound/lost_limb/W in parent.wounds) - if(W.limb_tag == organ_tag) - qdel(W) //Removes itself from parent.wounds + for(var/datum/wound/lost_limb/stump in parent.wounds) + if(stump.limb_tag == organ_tag) + qdel(stump) //Removes itself from parent.wounds break if(!in_place) @@ -586,8 +584,8 @@ if(BURN) src.heal_damage(0, repair_amount, 0, 1) owner.try_refresh_visible_overlays() if(user == src.owner) - var/decl/pronouns/G = user.get_pronouns() - user.visible_message(SPAN_NOTICE("\The [user] patches [damage_desc] on [G.his] [name] with \the [tool].")) + var/decl/pronouns/user_pronouns = user.get_pronouns() + user.visible_message(SPAN_NOTICE("\The [user] patches [damage_desc] on [user_pronouns.his] [name] with \the [tool].")) else user.visible_message(SPAN_NOTICE("\The [user] patches [damage_desc] on \the [owner]'s [name] with \the [tool].")) return 1 @@ -631,11 +629,11 @@ This function completely restores a damaged organ to perfect condition. /obj/item/organ/external/remove_rejuv() if(owner) owner.remove_organ(src, FALSE, FALSE, TRUE, TRUE, FALSE) - for(var/obj/item/organ/external/E in children) - E.remove_rejuv() + for(var/obj/item/organ/external/child in children) + child.remove_rejuv() LAZYCLEARLIST(children) - for(var/obj/item/organ/internal/I in internal_organs) - I.remove_rejuv() + for(var/obj/item/organ/internal/internal_organ in internal_organs) + internal_organ.remove_rejuv() ..() /obj/item/organ/external/proc/createwound(var/type = CUT, var/damage, var/surgical) @@ -680,44 +678,44 @@ This function completely restores a damaged organ to perfect condition. if((type == CUT || type == BRUISE) && damage >= 5) //we need to make sure that the wound we are going to worsen is compatible with the type of damage... var/list/compatible_wounds = list() - for (var/datum/wound/W in wounds) - if (W.can_worsen(type, damage)) - compatible_wounds += W + for (var/datum/wound/wound in wounds) + if (wound.can_worsen(type, damage)) + compatible_wounds += wound if(compatible_wounds.len) - var/datum/wound/W = pick(compatible_wounds) - W.open_wound(damage) + var/datum/wound/wound = pick(compatible_wounds) + wound.open_wound(damage) if(owner && prob(25)) if(BP_IS_CRYSTAL(src)) - owner.visible_message("The cracks in \the [owner]'s [name] spread.",\ - "The cracks in your [name] spread.",\ - "You hear the cracking of crystal.") + owner.visible_message(SPAN_DANGER("The cracks in \the [owner]'s [name] spread."),\ + SPAN_DANGER("The cracks in your [name] spread."),\ + SPAN_DANGER("You hear the cracking of crystal.")) else if(BP_IS_PROSTHETIC(src)) - owner.visible_message("The damage to \the [owner]'s [name] worsens.",\ - "The damage to your [name] worsens.",\ - "You hear the screech of abused metal.") + owner.visible_message(SPAN_DANGER("The damage to \the [owner]'s [name] worsens."),\ + SPAN_DANGER("The damage to your [name] worsens."),\ + SPAN_DANGER("You hear the screech of abused metal.")) else - owner.visible_message("The wound on \the [owner]'s [name] widens with a nasty ripping noise.",\ - "The wound on your [name] widens with a nasty ripping noise.",\ - "You hear a nasty ripping noise, as if flesh is being torn apart.") - return W + owner.visible_message(SPAN_DANGER("The wound on \the [owner]'s [name] widens with a nasty ripping noise."),\ + SPAN_DANGER("The wound on your [name] widens with a nasty ripping noise."),\ + SPAN_DANGER("You hear a nasty ripping noise, as if flesh is being torn apart.")) + return wound //Creating wound var/wound_type = get_wound_type(type, damage) if(wound_type) - var/datum/wound/W = new wound_type(damage, src, surgical) + var/datum/wound/wound = new wound_type(damage, src, surgical) //Check whether we can add the wound to an existing wound if(surgical) - W.autoheal_cutoff = 0 + wound.autoheal_cutoff = 0 else for(var/datum/wound/other in wounds) - if(other.can_merge_wounds(W)) - other.merge_wound(W) + if(other.can_merge_wounds(wound)) + other.merge_wound(wound) return - LAZYADD(wounds, W) - return W + LAZYADD(wounds, wound) + return wound /**************************************************** PROCESSING & UPDATING @@ -745,8 +743,8 @@ This function completely restores a damaged organ to perfect condition. if(get_genetic_damage()) return TRUE - for(var/obj/item/organ/internal/I in internal_organs) - if(I.getToxLoss()) + for(var/obj/item/organ/internal/internal_organ in internal_organs) + if(internal_organ.getToxLoss()) return TRUE if(last_dam != brute_dam + burn_dam) // Process when we are fully healed up. @@ -809,18 +807,18 @@ Note that amputating the affected organ does in fact remove the infection from t handle_germ_effects() /obj/item/organ/external/proc/handle_germ_sync() - var/turf/T = get_turf(owner) - for(var/datum/wound/W in wounds) + var/turf/current_turf = get_turf(owner) + for(var/datum/wound/wound in wounds) //Open wounds can become infected - // what in the hell is this doing with T? - if(max(istype(T) && T.simulated && T.get_dirt()*10, 2*owner.germ_level) > W.germ_level && W.infection_check()) - W.germ_level++ + // what in the hell is this doing with current_turf? + if(max(istype(current_turf) && current_turf.simulated && current_turf.get_dirt()*10, 2*owner.germ_level) > wound.germ_level && wound.infection_check()) + wound.germ_level++ var/antibiotics = GET_CHEMICAL_EFFECT(owner, CE_ANTIBIOTIC) if (!antibiotics) - for(var/datum/wound/W in wounds) + for(var/datum/wound/wound in wounds) //Infected wounds raise the organ's germ level - if (W.germ_level > germ_level || prob(min(W.germ_level, 30))) + if (wound.germ_level > germ_level || prob(min(wound.germ_level, 30))) germ_level++ break //limit increase to a maximum of one per second @@ -834,17 +832,17 @@ Note that amputating the affected organ does in fact remove the infection from t if(germ_level >= INFECTION_LEVEL_TWO) //spread the infection to internal organs var/obj/item/organ/target_organ = null //make internal organs become infected one at a time instead of all at once - for (var/obj/item/organ/I in internal_organs) - if (I.germ_level > 0 && I.germ_level < min(germ_level, INFECTION_LEVEL_TWO)) //once the organ reaches whatever we can give it, or level two, switch to a different one - if (!target_organ || I.germ_level > target_organ.germ_level) //choose the organ with the highest germ_level - target_organ = I + for (var/obj/item/organ/internal_organ in internal_organs) + if (internal_organ.germ_level > 0 && internal_organ.germ_level < min(germ_level, INFECTION_LEVEL_TWO)) //once the organ reaches whatever we can give it, or level two, switch to a different one + if (!target_organ || internal_organ.germ_level > target_organ.germ_level) //choose the organ with the highest germ_level + target_organ = internal_organ if (!target_organ) //figure out which organs we can spread germs to and pick one at random var/list/candidate_organs = list() - for (var/obj/item/organ/I in internal_organs) - if (I.germ_level < germ_level) - candidate_organs |= I + for (var/obj/item/organ/internal_organ in internal_organs) + if (internal_organ.germ_level < germ_level) + candidate_organs |= internal_organ if (candidate_organs.len) target_organ = pick(candidate_organs) @@ -865,7 +863,7 @@ Note that amputating the affected organ does in fact remove the infection from t if(germ_level >= INFECTION_LEVEL_THREE && antibiotics < REAGENTS_OVERDOSE) //overdosing is necessary to stop severe infections if (!(status & ORGAN_DEAD)) status |= ORGAN_DEAD - to_chat(owner, "You can't feel your [name] anymore...") + to_chat(owner, SPAN_NOTICE("You can't feel your [name] anymore...")) owner.update_body(1) germ_level++ @@ -876,18 +874,18 @@ Note that amputating the affected organ does in fact remove the infection from t var/update_surgery if(BP_IS_PROSTHETIC(src) || BP_IS_CRYSTAL(src)) //Robotic limbs don't heal or get worse. - for(var/datum/wound/W in wounds) //Repaired wounds disappear though - if(W.damage <= 0) //and they disappear right away - qdel(W) //TODO: robot wounds for robot limbs + for(var/datum/wound/wound in wounds) //Repaired wounds disappear though + if(wound.damage <= 0) //and they disappear right away + qdel(wound) //TODO: robot wounds for robot limbs update_surgery = TRUE if(owner && update_surgery) owner.update_surgery() return - for(var/datum/wound/W in wounds) + for(var/datum/wound/wound in wounds) // wounds can disappear after 10 minutes at the earliest - if(W.damage <= 0 && W.created + (10 MINUTES) <= world.time) - qdel(W) + if(wound.damage <= 0 && wound.created + (10 MINUTES) <= world.time) + qdel(wound) update_surgery = TRUE continue // let the GC handle the deletion of the wound @@ -895,7 +893,7 @@ Note that amputating the affected organ does in fact remove the infection from t // slow healing var/heal_amt = 0 // if damage >= 50 AFTER treatment then it's probably too severe to heal within the timeframe of a round. - if (owner && !GET_CHEMICAL_EFFECT(owner, CE_TOXIN) && W.can_autoheal() && W.wound_damage() && brute_ratio < 0.5 && burn_ratio < 0.5) + if (owner && !GET_CHEMICAL_EFFECT(owner, CE_TOXIN) && wound.can_autoheal() && wound.wound_damage() && brute_ratio < 0.5 && burn_ratio < 0.5) heal_amt += 0.5 // we only update wounds once in [wound_update_accuracy] ticks so have to emulate realtime @@ -912,10 +910,10 @@ Note that amputating the affected organ does in fact remove the infection from t // making it look prettier on scanners heal_amt = round(heal_amt,0.1) var/dam_type = BRUTE - if(W.damage_type == BURN) + if(wound.damage_type == BURN) dam_type = BURN if(owner?.can_autoheal(dam_type)) - W.heal_damage(heal_amt) + wound.heal_damage(heal_amt) // sync the organ's damage with its wounds update_damages() @@ -933,29 +931,30 @@ Note that amputating the affected organ does in fact remove the infection from t status &= ~ORGAN_BLEEDING var/clamped = 0 - var/mob/living/human/H + // This is defined outside of the loop as an optimization for a large number of wounds. + var/mob/living/human/human_owner if(ishuman(owner)) - H = owner + human_owner = owner //update damage counts var/bleeds = (!BP_IS_PROSTHETIC(src) && !BP_IS_CRYSTAL(src)) - for(var/datum/wound/W in wounds) + for(var/datum/wound/wound in wounds) - if(W.damage <= 0) - qdel(W) + if(wound.damage <= 0) + qdel(wound) continue - if(W.damage_type == BURN) - burn_dam += W.damage + if(wound.damage_type == BURN) + burn_dam += wound.damage else - brute_dam += W.damage + brute_dam += wound.damage - if(bleeds && W.bleeding() && (H && H.should_have_organ(BP_HEART))) - W.bleed_timer-- + if(bleeds && wound.bleeding() && (human_owner && human_owner.should_have_organ(BP_HEART))) + wound.bleed_timer-- status |= ORGAN_BLEEDING - clamped |= W.clamped - number_wounds += W.amount + clamped |= wound.clamped + number_wounds += wound.amount damage = brute_dam + burn_dam update_damage_ratios() @@ -1060,10 +1059,10 @@ Note that amputating the affected organ does in fact remove the infection from t . = new /obj/effect/decal/cleanable/blood/gibs(dropturf) if(species && istype(., /obj/effect/decal/cleanable/blood/gibs)) - var/obj/effect/decal/cleanable/blood/gibs/G = . - G.fleshcolor = species.get_species_flesh_color(owner) - G.basecolor = species.get_species_blood_color(owner) - G.update_icon() + var/obj/effect/decal/cleanable/blood/gibs/gibs = . + gibs.fleshcolor = species.get_species_flesh_color(owner) + gibs.basecolor = species.get_species_blood_color(owner) + gibs.update_icon() //Handles dismemberment /obj/item/organ/external/proc/dismember(var/clean, var/disintegrate = DISMEMBER_METHOD_EDGE, var/ignore_children, var/silent, var/ignore_last_organ) @@ -1078,9 +1077,9 @@ Note that amputating the affected organ does in fact remove the infection from t var/list/organ_msgs = get_droplimb_messages_for(disintegrate, clean) if(LAZYLEN(organ_msgs) >= 3) - owner.visible_message("[organ_msgs[1]]", \ - "[organ_msgs[2]]", \ - "[organ_msgs[3]]") + owner.visible_message(SPAN_DANGER("[organ_msgs[1]]"), \ + SPAN_MODERATE("[organ_msgs[2]]"), \ + SPAN_DANGER("[organ_msgs[3]]")) add_pain(60) if(!clean) @@ -1110,9 +1109,9 @@ Note that amputating the affected organ does in fact remove the infection from t original_parent.sever_artery() // Leave a big ol hole. - var/datum/wound/lost_limb/W = new(src, disintegrate, clean) - W.parent_organ = original_parent - LAZYADD(original_parent.wounds, W) + var/datum/wound/lost_limb/stump = new(src, disintegrate, clean) + stump.parent_organ = original_parent + LAZYADD(original_parent.wounds, stump) original_parent.update_damages() if(QDELETED(src)) @@ -1135,19 +1134,19 @@ Note that amputating the affected organ does in fact remove the infection from t var/atom/movable/gore = place_remains_from_dismember_method(disintegrate) if(gore) if(disintegrate == DISMEMBER_METHOD_BURN || disintegrate == DISMEMBER_METHOD_ACID) - for(var/obj/item/I in src) - if(I.w_class > ITEM_SIZE_SMALL && !istype(I,/obj/item/organ)) - I.dropInto(loc) + for(var/obj/item/contained_item in src) + if(contained_item.w_class > ITEM_SIZE_SMALL && !istype(contained_item, /obj/item/organ)) + contained_item.dropInto(loc) else if(disintegrate == DISMEMBER_METHOD_BLUNT) gore.throw_at(get_edge_target_turf(src,pick(global.alldirs)), rand(1,3), THROWFORCE_GIBS) - for(var/obj/item/organ/I in internal_organs) - I.do_uninstall() //No owner so run uninstall directly - I.dropInto(get_turf(loc)) - if(!QDELETED(I) && isturf(loc)) - I.throw_at(get_edge_target_turf(src,pick(global.alldirs)), rand(1,3), THROWFORCE_GIBS) - for(var/obj/item/I in src) - I.dropInto(loc) - I.throw_at(get_edge_target_turf(src,pick(global.alldirs)), rand(1,3), THROWFORCE_GIBS) + for(var/obj/item/organ/internal_organ in internal_organs) + internal_organ.do_uninstall() //No owner so run uninstall directly + internal_organ.dropInto(get_turf(loc)) + if(!QDELETED(internal_organ) && isturf(loc)) + internal_organ.throw_at(get_edge_target_turf(src,pick(global.alldirs)), rand(1,3), THROWFORCE_GIBS) + for(var/obj/item/contained_item in src) + contained_item.dropInto(loc) + contained_item.throw_at(get_edge_target_turf(src,pick(global.alldirs)), rand(1,3), THROWFORCE_GIBS) if(!QDELETED(src)) qdel(src) @@ -1169,70 +1168,70 @@ Note that amputating the affected organ does in fact remove the infection from t // checks if all wounds on the organ are bandaged /obj/item/organ/external/proc/is_bandaged() - for(var/datum/wound/W in wounds) - if(!W.bandaged) + for(var/datum/wound/wound in wounds) + if(!wound.bandaged) return 0 return 1 // checks if all wounds on the organ are salved /obj/item/organ/external/proc/is_salved() - for(var/datum/wound/W in wounds) - if(!W.salved) + for(var/datum/wound/wound in wounds) + if(!wound.salved) return 0 return 1 // checks if all wounds on the organ are disinfected /obj/item/organ/external/proc/is_disinfected() - for(var/datum/wound/W in wounds) - if(!W.disinfected) + for(var/datum/wound/wound in wounds) + if(!wound.disinfected) return 0 return 1 /obj/item/organ/external/proc/salve() var/rval = 0 - for(var/datum/wound/W in wounds) - rval |= !W.salved - W.salved = 1 + for(var/datum/wound/wound in wounds) + rval |= !wound.salved + wound.salved = 1 return rval /obj/item/organ/external/proc/disinfect() var/rval = 0 - for(var/datum/wound/W in wounds) - rval |= !W.disinfected - W.disinfected = 1 - W.germ_level = 0 + for(var/datum/wound/wound in wounds) + rval |= !wound.disinfected + wound.disinfected = 1 + wound.germ_level = 0 return rval /obj/item/organ/external/proc/clamp_organ() var/rval = 0 src.status &= ~ORGAN_BLEEDING - for(var/datum/wound/W in wounds) - rval |= !W.clamped - W.clamped = 1 + for(var/datum/wound/wound in wounds) + rval |= !wound.clamped + wound.clamped = 1 return rval /obj/item/organ/external/proc/clamped() - for(var/datum/wound/W in wounds) - if(W.clamped) + for(var/datum/wound/wound in wounds) + if(wound.clamped) return 1 /obj/item/organ/external/proc/remove_clamps() var/rval = 0 - for(var/datum/wound/W in wounds) - rval |= W.clamped - W.clamped = 0 + for(var/datum/wound/wound in wounds) + rval |= wound.clamped + wound.clamped = 0 return rval // open incisions and expose implants // this is the retract step of surgery /obj/item/organ/external/proc/open_incision() - var/datum/wound/W = get_incision() - if(!W) return - W.open_wound(min(W.damage * 2, W.damage_list[1] - W.damage)) + var/datum/wound/incision = get_incision() + if(!incision) return + incision.open_wound(min(incision.damage * 2, incision.damage_list[1] - incision.damage)) if(!encased) - for(var/obj/item/implant/I in implants) - I.exposed() + for(var/obj/item/implant/implant in implants) + implant.exposed() /obj/item/organ/external/proc/fracture() if(!get_config_value(/decl/config/toggle/on/health_bones_can_break)) @@ -1244,9 +1243,9 @@ Note that amputating the affected organ does in fact remove the infection from t if(owner) owner.visible_message(\ - "You hear a loud cracking sound coming from \the [owner].",\ - "Something feels like it shattered in your [name]!",\ - "You hear a sickening crack.") + SPAN_DANGER("You hear a loud cracking sound coming from \the [owner]."),\ + SPAN_DANGER("Something feels like it shattered in your [name]!"),\ + SPAN_DANGER("You hear a sickening crack.")) jostle_bone() if(can_feel_pain()) owner.emote(/decl/emote/audible/scream) @@ -1305,8 +1304,8 @@ Note that amputating the affected organ does in fact remove the infection from t return (brute_dam+burn_dam) //could use max_damage? /obj/item/organ/external/proc/has_infected_wound() - for(var/datum/wound/W in wounds) - if(W.germ_level > INFECTION_LEVEL_ONE) + for(var/datum/wound/wound in wounds) + if(wound.germ_level > INFECTION_LEVEL_ONE) return 1 return 0 @@ -1327,38 +1326,38 @@ Note that amputating the affected organ does in fact remove the infection from t /obj/item/organ/external/proc/is_malfunctioning() return (is_robotic() && (brute_dam + burn_dam) >= 10 && prob(brute_dam + burn_dam)) -/obj/item/organ/external/proc/embed_in_organ(var/obj/item/W, var/silent = FALSE, var/supplied_message, var/datum/wound/supplied_wound) +/obj/item/organ/external/proc/embed_in_organ(var/obj/item/embedding, var/silent = FALSE, var/supplied_message, var/datum/wound/supplied_wound) if(!owner || loc != owner) return if(species.species_flags & SPECIES_FLAG_NO_EMBED) return if(!silent) if(supplied_message) - owner.visible_message("[supplied_message]") + owner.visible_message(SPAN_DANGER("[supplied_message]")) else - owner.visible_message("\The [W] sticks in the wound!") + owner.visible_message(SPAN_DANGER("\The [embedding] sticks in the wound!")) if(!supplied_wound) for(var/datum/wound/wound in wounds) - if((wound.damage_type == CUT || wound.damage_type == PIERCE) && wound.damage >= W.w_class * 5) + if((wound.damage_type == CUT || wound.damage_type == PIERCE) && wound.damage >= embedding.w_class * 5) supplied_wound = wound break if(!supplied_wound) - supplied_wound = createwound(PIERCE, W.w_class * 5) + supplied_wound = createwound(PIERCE, embedding.w_class * 5) - if(!supplied_wound || (W in supplied_wound.embedded_objects)) // Just in case. + if(!supplied_wound || (embedding in supplied_wound.embedded_objects)) // Just in case. return - LAZYDISTINCTADD(supplied_wound.embedded_objects, W) - LAZYDISTINCTADD(implants, W) + LAZYDISTINCTADD(supplied_wound.embedded_objects, embedding) + LAZYDISTINCTADD(implants, embedding) owner.embedded_flag = 1 owner.verbs += /mob/proc/yank_out_object - W.add_blood(owner) - if(ismob(W.loc)) - var/mob/living/H = W.loc - H.drop_from_inventory(W) - W.forceMove(owner) + embedding.add_blood(owner) + if(ismob(embedding.loc)) + var/mob/living/holder = embedding.loc + holder.drop_from_inventory(embedding) + embedding.forceMove(owner) /obj/item/organ/external/do_uninstall(in_place, detach, ignore_children, update_icon) @@ -1369,11 +1368,11 @@ Note that amputating the affected organ does in fact remove the infection from t if(victim) if(in_place) //When removing in place, we don't bother with moving child organs and implants, we just clear the refs - for(var/obj/item/implant/I in implants) - I.removed() + for(var/obj/item/implant/implant in implants) + implant.removed() //Remove the parent ref from all childs limbs until we replace the organ in place - for(var/obj/item/organ/external/E in children) - E.parent = null + for(var/obj/item/organ/external/child in children) + child.parent = null implants = null children = null @@ -1382,28 +1381,28 @@ Note that amputating the affected organ does in fact remove the infection from t //Move over our implants/items into us, and drop whatever else is too big or not an object(??) for(var/atom/movable/implant in implants) //large items and non-item objs fall to the floor, everything else stays - var/obj/item/I = implant + var/obj/item/item_implant = implant if(QDELETED(implant)) LAZYREMOVE(implants, implant) continue - if(istype(I) && I.w_class < ITEM_SIZE_NORMAL) - if(istype(I, /obj/item/implant)) - var/obj/item/implant/imp = I + if(istype(item_implant) && item_implant.w_class < ITEM_SIZE_NORMAL) + if(istype(item_implant, /obj/item/implant)) + var/obj/item/implant/imp = item_implant imp.removed() implant.forceMove(src) - else - //Dumpt the rest on the turf + else // Is this even necessary? What non-items can even get added to implants? + //Dump the rest on the turf LAZYREMOVE(implants, implant) implant.forceMove(get_turf(src)) if(!ignore_children) //Move our chilren limb into our contents - for(var/obj/item/organ/external/O in children) - victim.remove_organ(O, FALSE, FALSE, FALSE, in_place, update_icon) - if(QDELETED(O)) - LAZYREMOVE(children, O) + for(var/obj/item/organ/external/child in children) + victim.remove_organ(child, FALSE, FALSE, FALSE, in_place, update_icon) + if(QDELETED(child)) + LAZYREMOVE(children, child) continue - O.do_install(null, src, FALSE, update_icon, FALSE) //Forcemove the organ and properly set it up in our internal data + child.do_install(null, src, FALSE, update_icon, FALSE) //Forcemove the organ and properly set it up in our internal data // Grab all the children internal organs for(var/obj/item/organ/internal/organ in internal_organs) @@ -1448,13 +1447,13 @@ Note that amputating the affected organ does in fact remove the infection from t return if(owner) if(type == BRUTE) - owner.visible_message("You hear a sickening cracking sound coming from \the [owner]'s [name].", \ - "Your [name] becomes a mangled mess!", \ - "You hear a sickening crack.") + owner.visible_message(SPAN_DANGER("You hear a sickening cracking sound coming from \the [owner]'s [name]."), \ + SPAN_DANGER("Your [name] becomes a mangled mess!"), \ + SPAN_DANGER("You hear a sickening crack.")) else - owner.visible_message("\The [owner]'s [name] melts away, turning into mangled mess!", \ - "Your [name] melts away!", \ - "You hear a sickening sizzle.") + owner.visible_message(SPAN_DANGER("\The [owner]'s [name] melts away, turning into mangled mess!"), \ + SPAN_DANGER("Your [name] melts away!"), \ + SPAN_DANGER("You hear a sickening sizzle.")) status |= ORGAN_DISFIGURED /obj/item/organ/external/proc/get_incision(var/strict) @@ -1465,20 +1464,20 @@ Note that amputating the affected organ does in fact remove the infection from t if(!incision || incision.damage < other.damage) incision = other else - for(var/datum/wound/cut/W in wounds) - if(!W.is_open()) // Shit's unusable + for(var/datum/wound/cut/candidate_incision in wounds) + if(!candidate_incision.is_open()) // Shit's unusable continue - if(strict && !W.is_surgical()) //We don't need dirty ones + if(strict && !candidate_incision.is_surgical()) //We don't need dirty ones continue if(!incision) - incision = W + incision = candidate_incision continue - var/same = W.is_surgical() == incision.is_surgical() + var/same = candidate_incision.is_surgical() == incision.is_surgical() if(same) //If they're both dirty or both are surgical, just get bigger one - if(W.damage > incision.damage) - incision = W - else if(W.is_surgical()) //otherwise surgical one takes priority - incision = W + if(candidate_incision.damage > incision.damage) + incision = candidate_incision + else if(candidate_incision.is_surgical()) //otherwise surgical one takes priority + incision = candidate_incision return incision /obj/item/organ/external/proc/how_open() @@ -1510,8 +1509,8 @@ Note that amputating the affected organ does in fact remove the infection from t return if(LAZYLEN(internal_organs) && prob(brute_dam + force)) owner.custom_pain("A piece of bone in your [encased ? encased : name] moves painfully!", 50, affecting = src) - var/obj/item/organ/internal/I = pick(internal_organs) - I.take_internal_damage(rand(3,5)) + var/obj/item/organ/internal/internal_organ = pick(internal_organs) + internal_organ.take_internal_damage(rand(3,5)) /obj/item/organ/external/proc/jointlock(mob/attacker) if(!can_feel_pain()) @@ -1519,7 +1518,7 @@ Note that amputating the affected organ does in fact remove the infection from t var/armor = 100 * owner.get_blocked_ratio(owner, BRUTE, damage = 30) if(armor < 70) - to_chat(owner, "You feel extreme pain!") + to_chat(owner, SPAN_DANGER("You feel extreme pain!")) var/max_halloss = round(owner.species.total_health * 0.8 * ((100 - armor) / 100)) //up to 80% of passing out, further reduced by armour add_pain(clamp(0, max_halloss - owner.get_damage(PAIN), 30)) @@ -1529,18 +1528,18 @@ Note that amputating the affected organ does in fact remove the infection from t var/key = used_weapon var/data = used_weapon if(istype(used_weapon, /obj/item)) - var/obj/item/I = used_weapon - key = I.name - data = english_list(I.get_autopsy_descriptors()) - var/datum/autopsy_data/W = LAZYACCESS(autopsy_data, key) - if(!W) - W = new() - W.weapon = data - LAZYSET(autopsy_data, key, W) - - W.hits += 1 - W.damage += damage - W.time_inflicted = world.time + var/obj/item/used_item = used_weapon + key = used_item.name + data = english_list(used_item.get_autopsy_descriptors()) + var/datum/autopsy_data/autopsy_datum = LAZYACCESS(autopsy_data, key) + if(!autopsy_datum) + autopsy_datum = new() + autopsy_datum.weapon = data + LAZYSET(autopsy_data, key, autopsy_datum) + + autopsy_datum.hits += 1 + autopsy_datum.damage += damage + autopsy_datum.time_inflicted = world.time /obj/item/organ/external/proc/has_genitals() return !BP_IS_PROSTHETIC(src) && bodytype?.get_vulnerable_location() == organ_tag @@ -1600,8 +1599,8 @@ Note that amputating the affected organ does in fact remove the infection from t if(isnull(vital_to_owner)) . = ..() if(!.) - for(var/obj/item/organ/O in children) - if(O.is_vital_to_owner()) + for(var/obj/item/organ/child in children) + if(child.is_vital_to_owner()) vital_to_owner = TRUE break return vital_to_owner diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm index 4ced3c84d5a..217cd539aa3 100644 --- a/code/modules/organs/internal/_internal.dm +++ b/code/modules/organs/internal/_internal.dm @@ -279,7 +279,7 @@ brainmob.languages = M.languages?.Copy() brainmob.default_language = M.default_language to_chat(brainmob, SPAN_NOTICE("You feel slightly disoriented. That's normal when you're just \a [initial(src.name)].")) - callHook("debrain", list(brainmob)) + RAISE_EVENT(/decl/observ/debrain, brainmob, src, M) return TRUE return FALSE diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 960f437fde0..09774614695 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -19,6 +19,10 @@ tracer_type = /obj/effect/projectile/tracer/laser impact_type = /obj/effect/projectile/impact/laser +/obj/item/projectile/beam/megabot + damage = 45 + distance_falloff = 0.5 + /obj/item/projectile/beam/variable muzzle_type = /obj/effect/projectile/muzzle/variable tracer_type = /obj/effect/projectile/tracer/variable diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index 33165821ff6..111008ff5d7 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -27,11 +27,22 @@ icon_state= "bolter" damage = 50 damage_flags = DAM_BULLET | DAM_SHARP | DAM_EDGE + var/gyro_devastation = -1 + var/gyro_heavy_impact = 0 + var/gyro_light_impact = 2 /obj/item/projectile/bullet/gyro/on_hit(var/atom/target, var/blocked = 0) - explosion(target, -1, 0, 2) + target = get_turf(target) + if(istype(target)) + explosion(target, gyro_devastation, gyro_heavy_impact, gyro_light_impact) return 1 +/obj/item/projectile/bullet/gyro/microrocket + name = "microrocket" + distance_falloff = 1.3 + fire_sound = 'sound/effects/Explosion1.ogg' + gyro_light_impact = 1 + /obj/item/projectile/temp name = "freeze beam" icon_state = "ice_2" diff --git a/code/modules/random_map/drop/droppod.dm b/code/modules/random_map/drop/droppod.dm index c84dd4cb6ea..1701c7e77fe 100644 --- a/code/modules/random_map/drop/droppod.dm +++ b/code/modules/random_map/drop/droppod.dm @@ -15,7 +15,7 @@ floor_type = /turf/floor/reinforced var/list/supplied_drop_types = list() var/door_type = /obj/structure/droppod_door - var/drop_type = /mob/living/simple_animal/hostile/retaliate/parrot + var/drop_type = /mob/living/simple_animal/hostile/parrot var/auto_open_doors var/placement_explosion_dev = 1 diff --git a/code/modules/reagents/reactions/_reaction.dm b/code/modules/reagents/reactions/_reaction.dm index 7725ffdff81..db0c252978a 100644 --- a/code/modules/reagents/reactions/_reaction.dm +++ b/code/modules/reagents/reactions/_reaction.dm @@ -14,7 +14,7 @@ var/reaction_sound = 'sound/effects/bubbles.ogg' var/lore_text var/mechanics_text - var/reaction_category + var/reaction_category = REACTION_TYPE_COMPOUND /// Flags used when reaction processing. var/chemical_reaction_flags = 0 diff --git a/code/modules/reagents/reactions/reaction_compounds.dm b/code/modules/reagents/reactions/reaction_compounds.dm index 25fe15e5b28..e27f62157e4 100644 --- a/code/modules/reagents/reactions/reaction_compounds.dm +++ b/code/modules/reagents/reactions/reaction_compounds.dm @@ -1,6 +1,5 @@ /decl/chemical_reaction/compound abstract_type = /decl/chemical_reaction/compound - reaction_category = REACTION_TYPE_COMPOUND /decl/chemical_reaction/compound/surfactant name = "Azosurfactant" diff --git a/code/modules/reagents/reactions/reaction_grenade_reaction.dm b/code/modules/reagents/reactions/reaction_grenade_reaction.dm index dd3aaac6be8..a55899a97ea 100644 --- a/code/modules/reagents/reactions/reaction_grenade_reaction.dm +++ b/code/modules/reagents/reactions/reaction_grenade_reaction.dm @@ -3,6 +3,7 @@ abstract_type = /decl/chemical_reaction/grenade_reaction result_amount = 1 chemical_reaction_flags = CHEM_REACTION_FLAG_OVERFLOW_CONTAINER + reaction_category = REACTION_TYPE_COMPOUND /decl/chemical_reaction/grenade_reaction/explosion_potassium name = "Explosion" diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index aa9eb80cfff..92ebdc04212 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -31,7 +31,7 @@ /obj/structure/iv_drip, /obj/machinery/disposal, /mob/living/simple_animal/cow, - /mob/living/simple_animal/hostile/retaliate/goat, + /mob/living/simple_animal/hostile/goat, /obj/machinery/sleeper, /obj/machinery/smartfridge/, /obj/machinery/biogenerator, diff --git a/code/modules/recycling/disposalholder.dm b/code/modules/recycling/disposalholder.dm index 120782aaff4..4f3546d06a6 100644 --- a/code/modules/recycling/disposalholder.dm +++ b/code/modules/recycling/disposalholder.dm @@ -18,9 +18,9 @@ var/partialTag = "" //set by a partial tagger the first time round, then put in destinationTag if it goes through again. // initialize a holder from the contents of a disposal unit -/obj/structure/disposalholder/proc/init(var/obj/machinery/disposal/D, var/datum/gas_mixture/flush_gas) +/obj/structure/disposalholder/proc/init(var/obj/machinery/disposal/disposal_unit, var/datum/gas_mixture/flush_gas) gas = flush_gas// transfer gas resv. into holder object -- let's be explicit about the data this proc consumes, please. - var/stuff = D.get_contained_external_atoms() + var/stuff = disposal_unit.get_contained_external_atoms() //Check for any living mobs trigger hasmob. //hasmob effects whether the package goes to cargo or its tagged destination. hasmob = length(check_mob(stuff)) @@ -36,20 +36,20 @@ /obj/structure/disposalholder/proc/check_mob(list/stuff, max_depth = 1) . = list() if(max_depth > 0) - for(var/mob/living/M in stuff) - if (!isdrone(M)) - . += M - for(var/obj/O in stuff) - . += check_mob(O.contents, max_depth - 1) + for(var/mob/living/victim in stuff) + if (!isdrone(victim)) + . += victim + for(var/obj/container in stuff) + . += check_mob(container.contents, max_depth - 1) // start the movement process // argument is the disposal unit the holder started in -/obj/structure/disposalholder/proc/start(var/obj/machinery/disposal/D) - if(!D.trunk) - D.expel(src) // no trunk connected, so expel immediately +/obj/structure/disposalholder/proc/start(var/obj/machinery/disposal/disposal_unit) + if(!disposal_unit.trunk) + disposal_unit.expel(src) // no trunk connected, so expel immediately return - forceMove(D.trunk) + forceMove(disposal_unit.trunk) active = 1 set_dir(DOWN) START_PROCESSING(SSdisposals, src) @@ -65,8 +65,8 @@ var/obj/structure/disposalpipe/last if(hasmob && prob(3)) - for(var/mob/living/H in check_mob(src)) - H.apply_damage(30, BRUTE, null, DAM_DISPERSED, "Blunt Trauma", ARMOR_MELEE_MAJOR)//horribly maim any living creature jumping down disposals. c'est la vie + for(var/mob/living/victim in check_mob(src)) + victim.apply_damage(30, BRUTE, null, DAM_DISPERSED, "Blunt Trauma", ARMOR_MELEE_MAJOR)//horribly maim any living creature jumping down disposals. c'est la vie var/obj/structure/disposalpipe/curr = loc if(!istype(curr)) @@ -87,26 +87,26 @@ return get_step(loc,dir) // find a matching pipe on a turf -/obj/structure/disposalholder/proc/findpipe(var/turf/T) - if(!T) +/obj/structure/disposalholder/proc/findpipe(var/turf/containing_turf) + if(!containing_turf) return null var/fdir = turn(dir, 180) // flip the movement direction - for(var/obj/structure/disposalpipe/P in T) - if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir - return P + for(var/obj/structure/disposalpipe/pipe in containing_turf) + if(fdir & pipe.dpdir) // find pipe direction mask that matches flipped dir + return pipe // if no matching pipe, return null return null // merge two holder objects // used when a a holder meets a stuck holder /obj/structure/disposalholder/proc/merge(var/obj/structure/disposalholder/other) - for(var/atom/movable/AM in other) - AM.forceMove(src) // move everything in other holder to this one - if(ismob(AM)) - var/mob/M = AM - if(M.client) // if a client mob, update eye to follow this holder - M.client.eye = src + for(var/atom/movable/other_movable in other) + other_movable.forceMove(src) // move everything in other holder to this one + if(ismob(other_movable)) + var/mob/other_mob = other_movable + if(other_mob.client) // if a client mob, update eye to follow this holder + other_mob.client.eye = src qdel(other) /obj/structure/disposalholder/proc/settag(var/new_tag) @@ -124,12 +124,12 @@ if(!isliving(user)) return - var/mob/living/U = user + var/mob/living/living_user = user - if (U.stat || U.is_on_special_ability_cooldown()) + if (living_user.stat || living_user.is_on_special_ability_cooldown()) return - U.set_special_ability_cooldown(10 SECONDS) + living_user.set_special_ability_cooldown(10 SECONDS) var/turf/our_turf = get_turf(src) if (our_turf) diff --git a/code/modules/sealant_gun/sealant.dm b/code/modules/sealant_gun/sealant.dm index 33e4b8d4a00..bd3abc985a2 100644 --- a/code/modules/sealant_gun/sealant.dm +++ b/code/modules/sealant_gun/sealant.dm @@ -38,7 +38,7 @@ break_apart(user) return TRUE -/obj/item/sealant/attackby(obj/item/W, mob/user) +/obj/item/sealant/attackby(obj/item/used_item, mob/user) break_apart(user) return TRUE @@ -54,9 +54,9 @@ user.setClickCooldown(1 SECOND) qdel(src) -/obj/item/sealant/Bump(atom/A, forced) +/obj/item/sealant/Bump(atom/bumped, forced) . = ..() - splat(A) + splat(bumped) /obj/item/sealant/throw_impact(atom/hit_atom) . = ..() @@ -66,18 +66,18 @@ if(splatted) return splatted = TRUE - var/turf/T = get_turf(target) || get_turf(src) - if(T) - new /obj/effect/sealant(T) + var/turf/target_turf = get_turf(target) || get_turf(src) + if(target_turf) + new /obj/effect/sealant(target_turf) if(isliving(target)) - var/mob/living/H = target + var/mob/living/living_target = target for(var/slot in shuffle(splat_try_equip_slots)) - if(!H.get_equipped_item(slot)) - H.equip_to_slot_if_possible(src, slot) - if(H.get_equipped_item(slot) == src) + if(!living_target.get_equipped_item(slot)) + living_target.equip_to_slot_if_possible(src, slot) + if(living_target.get_equipped_item(slot) == src) return - if(!T.density && !(locate(foam_type) in T)) - new foam_type(T) + if(!target_turf.density && !(locate(foam_type) in target_turf)) + new foam_type(target_turf) if(!QDELETED(src)) qdel(src) diff --git a/code/modules/shuttles/shuttle.dm b/code/modules/shuttles/shuttle.dm index 8003c296df8..2c9600fad90 100644 --- a/code/modules/shuttles/shuttle.dm +++ b/code/modules/shuttles/shuttle.dm @@ -255,8 +255,11 @@ // remove the old ceiling, if it existed for(var/turf/TO in turf_translation) var/turf/TA = GetAbove(TO) - if(istype(TA, ceiling_type)) - TA.ChangeTurf(get_base_turf_by_area(TA), TRUE, TRUE, TRUE) + if(istype(TA)) + if(istype(TA, ceiling_type)) + TA.ChangeTurf(get_base_turf_by_area(TA), TRUE, TRUE, TRUE) + else if(TA.prev_type == ceiling_type) + TA.prev_type = null handle_pipes_and_power_on_move(new_turfs) @@ -328,12 +331,21 @@ var/turf/TAS = GetAbove(TS) if(!istype(TAD)) continue + + // Check for multi-z shuttles. Don't create a ceiling where the shuttle is about to be. + if((istype(TAS) && (get_area(TAS) in shuttle_area))) + continue + + if(force || (istype(TAD, get_base_turf_by_area(TAD)) || TAD.is_open())) - // Check for multi-z shuttles. Don't create a ceiling where the shuttle is about to be. - if((istype(TAS) && (get_area(TAS) in shuttle_area))) - continue TAD.ChangeTurf(ceiling_type, TRUE, TRUE, TRUE) + // TODO: Ideally the latter checks here would remain is_open() rather than direct type checks, but unfortunately we can't do that with only the path. + // In nearly all current situations, they are effectively the same thing. + else if(!TAD.prev_type || istype(TAD.prev_type, get_base_turf_by_area(TAD)) || ispath(TAD.prev_type, /turf/open) || ispath(TAD.prev_type, /turf/space)) + // In case there's a pending shuttle move above, prepare it to create a ceiling post-translation. + TAD.prev_type = ceiling_type + //returns 1 if the shuttle has a valid arrive time /datum/shuttle/proc/has_arrive_time() return (moving_status == SHUTTLE_INTRANSIT) diff --git a/code/modules/species/species_bodytype.dm b/code/modules/species/species_bodytype.dm index 8169aa70bbb..2601b22ef1e 100644 --- a/code/modules/species/species_bodytype.dm +++ b/code/modules/species/species_bodytype.dm @@ -614,11 +614,11 @@ var/global/list/bodytypes_by_category = list() /decl/bodytype/proc/get_limb_from_zone(limb) . = length(LAZYACCESS(limb_mapping, limb)) ? pick(limb_mapping[limb]) : limb -/decl/bodytype/proc/check_vital_organ_missing(mob/living/H) +/decl/bodytype/proc/check_vital_organ_missing(mob/living/patient) if(length(vital_organs)) for(var/organ_tag in vital_organs) - var/obj/item/organ/O = H.get_organ(organ_tag, /obj/item/organ) - if(!O || (O.status & ORGAN_DEAD)) + var/obj/item/organ/vital_organ = patient.get_organ(organ_tag, /obj/item/organ) + if(!vital_organ || (vital_organ.status & ORGAN_DEAD)) return TRUE return FALSE diff --git a/code/modules/spells/aoe_turf/conjure/conjure.dm b/code/modules/spells/aoe_turf/conjure/conjure.dm index c50d700d8ba..9291456f41a 100644 --- a/code/modules/spells/aoe_turf/conjure/conjure.dm +++ b/code/modules/spells/aoe_turf/conjure/conjure.dm @@ -56,9 +56,7 @@ How they spawn stuff is decided by behaviour vars, which are explained below if(ismob(summoned_object)) //we want them to NOT attack us. var/mob/M = summoned_object M.faction = user.faction - for(var/varName in newVars) - if(varName in summoned_object.vars) - summoned_object.vars[varName] = newVars[varName] + apply_vars(summoned_object, user) if(duration) spawn(duration) @@ -68,4 +66,11 @@ How they spawn stuff is decided by behaviour vars, which are explained below return /spell/aoe_turf/conjure/proc/conjure_animation(var/atom/movable/overlay/animation, var/turf/target) - qdel(animation) \ No newline at end of file + qdel(animation) + +/spell/aoe_turf/conjure/proc/apply_vars(atom/summoned_object, mob/caster) + if(!istype(summoned_object) || !length(newVars)) + return + for(var/varName in newVars) + if(varName in summoned_object.vars) + summoned_object.vars[varName] = newVars[varName] diff --git a/code/modules/spells/aoe_turf/conjure/druidic_spells.dm b/code/modules/spells/aoe_turf/conjure/druidic_spells.dm index 823a1613525..e6f0b8ea260 100644 --- a/code/modules/spells/aoe_turf/conjure/druidic_spells.dm +++ b/code/modules/spells/aoe_turf/conjure/druidic_spells.dm @@ -67,9 +67,13 @@ hud_state = "wiz_bear" -/spell/aoe_turf/conjure/summon/bear/before_cast() - ..() - newVars["master"] = holder //why not do this in the beginning? MIND SWITCHING. +/spell/aoe_turf/conjure/summon/bear/apply_vars(atom/summoned_object, mob/caster) + . = ..() + if(isliving(summoned_object)) + var/mob/living/summoned_mob = summoned_object + if(istype(summoned_mob.ai, /datum/mob_controller/aggressive/commanded)) + var/datum/mob_controller/aggressive/commanded/command_ai = summoned_mob.ai + command_ai.master = caster /spell/aoe_turf/conjure/summon/bear/empower_spell() if(!..()) diff --git a/code/modules/spells/aoe_turf/conjure/faithful_hound.dm b/code/modules/spells/aoe_turf/conjure/faithful_hound.dm index 69ffe55db6c..4511e6a07f4 100644 --- a/code/modules/spells/aoe_turf/conjure/faithful_hound.dm +++ b/code/modules/spells/aoe_turf/conjure/faithful_hound.dm @@ -13,7 +13,16 @@ summon_type = list(/mob/living/simple_animal/faithful_hound) hud_state = "wiz_hound" + var/temp_password + +/spell/aoe_turf/conjure/faithful_hound/apply_vars(atom/summoned_object, mob/caster) + . = ..() + var/mob/living/simple_animal/faithful_hound/hound = summoned_object + if(istype(hound) && istype(hound.ai)) + hound.ai.add_friend(caster) + hound.ai.memorise(caster, temp_password) + temp_password = null + /spell/aoe_turf/conjure/faithful_hound/before_cast() ..() - var/password = sanitize(input("What password will this beast listen to?") as text, MAX_NAME_LEN) - newVars = list("password" = password, "allowed_mobs" = list(usr)) + temp_password = sanitize(input("What password will this beast listen to?") as text, MAX_NAME_LEN) diff --git a/code/modules/spells/artifacts/spellbound_servants.dm b/code/modules/spells/artifacts/spellbound_servants.dm index 2c8f93815c8..0f6bb21c84e 100644 --- a/code/modules/spells/artifacts/spellbound_servants.dm +++ b/code/modules/spells/artifacts/spellbound_servants.dm @@ -115,7 +115,7 @@ familiar_type = /mob/living/simple_animal/passive/mouse if("Cat") H.add_genetic_condition(GENE_COND_RUNNING) - familiar_type = /mob/living/simple_animal/cat + familiar_type = /mob/living/simple_animal/passive/cat if("Bear") familiar_type = /mob/living/simple_animal/hostile/bear var/spell/targeted/shapeshift/familiar/F = new() diff --git a/code/modules/spells/spellbook.dm b/code/modules/spells/spellbook.dm index 110fb98c471..b9ab445eeae 100644 --- a/code/modules/spells/spellbook.dm +++ b/code/modules/spells/spellbook.dm @@ -148,7 +148,7 @@ var/global/list/artefact_feedback = list( dat += " Make Contract" dat += "
[desc]

" dat += "
" - dat += "
Re-memorize your spellbook.
" + dat += "
Re-memorise your spellbook.
" if(spellbook.book_flags & INVESTABLE) if(investing_time) dat += "
Currently investing in a slot...
" @@ -233,10 +233,10 @@ var/global/list/artefact_feedback = list( investing_time = 0 has_sacrificed = 0 user.spellremove() - temp = "All spells and investments have been removed. You may now memorize a new set of spells." + temp = "All spells and investments have been removed. You may now memorise a new set of spells." SSstatistics.add_field_details("wizard_spell_learned","UM") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells else - to_chat(user, "You must be in the wizard academy to re-memorize your spells.") + to_chat(user, "You must be in the wizard academy to re-memorise your spells.") . = TOPIC_REFRESH src.interact(user) diff --git a/code/modules/spells/targeted/shapeshift.dm b/code/modules/spells/targeted/shapeshift.dm index 45262cdf540..8d32f265038 100644 --- a/code/modules/spells/targeted/shapeshift.dm +++ b/code/modules/spells/targeted/shapeshift.dm @@ -133,7 +133,7 @@ name = "Polymorph" desc = "This spell transforms the wizard into the common parrot." feedback = "AV" - possible_transformations = list(/mob/living/simple_animal/hostile/retaliate/parrot) + possible_transformations = list(/mob/living/simple_animal/hostile/parrot) drop_items = 0 share_damage = 0 @@ -151,7 +151,7 @@ name = "Corrupt Form" desc = "This spell shapes the wizard into a terrible, terrible beast." feedback = "CF" - possible_transformations = list(/mob/living/simple_animal/hostile/faithless) + possible_transformations = list(/mob/living/simple_animal/hostile/revenant) invocation = "mutters something dark and twisted as their form begins to twist..." invocation_type = SpI_EMOTE diff --git a/code/modules/submaps/submap_join.dm b/code/modules/submaps/submap_join.dm index 67ab23971a1..9084835d20c 100644 --- a/code/modules/submaps/submap_join.dm +++ b/code/modules/submaps/submap_join.dm @@ -111,7 +111,7 @@ SSticker.mode.handle_offsite_latejoin(character) global.universe.OnPlayerLatejoin(character) log_and_message_admins("has joined the round as offsite role [character.mind.assigned_role].", character) - callHook("submap_join", list(job, character)) + RAISE_EVENT(/decl/observ/submap_join, src, character, job) if(character.cannot_stand()) equip_wheelchair(character) job.post_equip_job_title(character, job.title) qdel(joining) diff --git a/code/modules/xenoarcheaology/artifacts/standalone/autocloner.dm b/code/modules/xenoarcheaology/artifacts/standalone/autocloner.dm index 487bb59f808..3fc25709061 100644 --- a/code/modules/xenoarcheaology/artifacts/standalone/autocloner.dm +++ b/code/modules/xenoarcheaology/artifacts/standalone/autocloner.dm @@ -22,17 +22,17 @@ /obj/machinery/auto_cloner/proc/get_passive_mob_types() . = list( - /mob/living/simple_animal/cat, + /mob/living/simple_animal/passive/cat, /mob/living/simple_animal/corgi, /mob/living/simple_animal/corgi/puppy, /mob/living/simple_animal/fowl/chicken, /mob/living/simple_animal/cow, - /mob/living/simple_animal/hostile/retaliate/parrot, + /mob/living/simple_animal/hostile/parrot, /mob/living/simple_animal/crab, /mob/living/simple_animal/passive/mouse, /mob/living/simple_animal/passive/mouse/rat, - /mob/living/simple_animal/hostile/retaliate/goat, - /mob/living/simple_animal/hostile/retaliate/goose + /mob/living/simple_animal/hostile/goat, + /mob/living/simple_animal/hostile/goose ) /obj/machinery/auto_cloner/Initialize() diff --git a/html/changelog.html b/html/changelog.html index 61796b46617..6857e44a3e5 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,14 @@ -->
+

06 July 2024

+

Penelope Haze updated:

+
    +
  • Ebony walls, floors, doors, and railings on Shaded Hills have all been replaced with walnut variants.
  • +
  • Most wall sconces now start unlit.
  • +
  • A full lantern now burns for an hour, but only holds 60 units of fuel.
  • +
+

20 June 2024

MistakeNot4892 updated:

    @@ -121,27 +129,6 @@

    MistakeNot4892 updated:

    • Mice will now flee from harm.
    - -

    08 May 2024

    -

    MistakeNot4892 updated:

    -
      -
    • Build mode ladders have been removed, and build mode relocate and move have been merged.
    • -
    - -

    06 May 2024

    -

    MistakeNot4892 updated:

    -
      -
    • Using a half-finished slapcrafting recipe in hand will dismantle it.
    • -
    • Crafting tools requires a binding material.
    • -
    • Serpentid can now leave pheremone traces with a set of emotes.
    • -
    - -

    04 May 2024

    -

    MistakeNot4892 updated:

    -
      -
    • Gibbing mobs or bodyparts will now drop usable meat and bone.
    • -
    • You can now craft hand axes.
    • -
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index e7f89d1c8cc..f95a0a0abb2 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -14775,3 +14775,9 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. MistakeNot4892: - tweak: You can now dip things like stacks of skin into barrels of water, wells or rivers. +2024-07-06: + Penelope Haze: + - tweak: Ebony walls, floors, doors, and railings on Shaded Hills have all been + replaced with walnut variants. + - tweak: Most wall sconces now start unlit. + - balance: A full lantern now burns for an hour, but only holds 60 units of fuel. diff --git a/icons/mob/simple_animal/faithless.dmi b/icons/mob/simple_animal/revenant.dmi similarity index 100% rename from icons/mob/simple_animal/faithless.dmi rename to icons/mob/simple_animal/revenant.dmi diff --git a/icons/mob/simple_animal/drake.dmi b/icons/mob/simple_animal/space_dragon.dmi similarity index 100% rename from icons/mob/simple_animal/drake.dmi rename to icons/mob/simple_animal/space_dragon.dmi diff --git a/maps/antag_spawn/wizard/wizard_base.dmm b/maps/antag_spawn/wizard/wizard_base.dmm index 1f7355801c5..dce6a0e9a6a 100644 --- a/maps/antag_spawn/wizard/wizard_base.dmm +++ b/maps/antag_spawn/wizard/wizard_base.dmm @@ -592,7 +592,7 @@ }, /area/map_template/wizard_station) "bB" = ( -/mob/living/simple_animal/hostile/retaliate/goat{ +/mob/living/simple_animal/hostile/goat{ name = "Experiment 97d" }, /turf/unsimulated/floor{ diff --git a/maps/away/errant_pisces/errant_pisces.dm b/maps/away/errant_pisces/errant_pisces.dm index fe26cfe4b7c..35c0e9ac80d 100644 --- a/maps/away/errant_pisces/errant_pisces.dm +++ b/maps/away/errant_pisces/errant_pisces.dm @@ -21,12 +21,15 @@ name = "cosmoshark" desc = "Enormous creature that resembles a shark with magenta glowing lines along its body and set of long deep-purple teeth." icon = 'maps/away/errant_pisces/icons/cosmoshark.dmi' - turns_per_wander = 5 butchery_data = /decl/butchery_data/animal/fish/space_carp/shark max_health = 100 natural_weapon = /obj/item/natural_weapon/bite/strong - break_stuff_probability = 35 faction = "shark" + ai = /datum/mob_controller/aggressive/carp/shark + +/datum/mob_controller/aggressive/carp/shark + break_stuff_probability = 35 + turns_per_wander = 10 /mob/living/simple_animal/hostile/carp/shark/carp_randomify() return @@ -41,10 +44,9 @@ environment.merge(sharkmaw_chlorine) visible_message(SPAN_WARNING("\The [src]'s body releases some gas from the gills with a quiet fizz!")) -/mob/living/simple_animal/hostile/carp/shark/attack_target(mob/target) - set waitfor = 0//to deal with sleep() possibly stalling other procs +/mob/living/simple_animal/hostile/carp/shark/apply_attack_effects(mob/living/target) . =..() - var/mob/living/L = . + var/mob/living/L = target if(istype(L)) if(prob(25))//if one is unlucky enough, they get tackled few tiles away L.visible_message("\The [src] tackles [L]!") diff --git a/maps/away/magshield/magshield.dm b/maps/away/magshield/magshield.dm index a6eb9bcec2e..f67bd55fee2 100644 --- a/maps/away/magshield/magshield.dm +++ b/maps/away/magshield/magshield.dm @@ -103,9 +103,10 @@ sleep(50) visible_message(SPAN_DANGER("\The [src] explodes!")) var/turf/T = get_turf(src) - explosion(T, 2, 3, 4, 10, 1) empulse(src, heavy_range*2, lighter_range*2, 1) - qdel(src) + explosion(T, 2, 3, 4, 10, 1) + if(!QDELETED(src)) + qdel(src) if(istype(W, /obj/item/mop)) to_chat(user, SPAN_NOTICE("You stick \the [W] into the rotating spokes, and it immediately breaks into tiny pieces.")) qdel(W) diff --git a/maps/away/mining/mining-orb.dmm b/maps/away/mining/mining-orb.dmm index d87a71a1cf0..f76fc99e386 100644 --- a/maps/away/mining/mining-orb.dmm +++ b/maps/away/mining/mining-orb.dmm @@ -347,7 +347,7 @@ /area/mine/unexplored) "SY" = ( /obj/structure/fountain, -/mob/living/simple_animal/hostile/retaliate/parrot/space, +/mob/living/simple_animal/hostile/parrot/space, /turf/floor/airless/stone, /area/mine/explored) "TP" = ( diff --git a/maps/away/slavers/slavers_base.dm b/maps/away/slavers/slavers_base.dm index e5be14c23cd..6d3104e84d2 100644 --- a/maps/away/slavers/slavers_base.dm +++ b/maps/away/slavers/slavers_base.dm @@ -133,20 +133,26 @@ name = "abolition extremist" desc = "Vigiliant fighter against slavery." icon = 'maps/away/slavers/icons/abolitionist.dmi' - speak_chance = 0 - turns_per_wander = 5 - stop_wandering_when_pulled = FALSE + max_health = 100 natural_weapon = /obj/item/natural_weapon/punch - can_escape = TRUE unsuitable_atmos_damage = 15 projectilesound = 'sound/weapons/laser.ogg' - ranged = 1 projectiletype = /obj/item/projectile/beam faction = "extremist abolitionists" + ai = /datum/mob_controller/abolitionist var/corpse = /obj/abstract/landmark/corpse/abolitionist var/weapon = /obj/item/gun/energy/laser +/mob/living/simple_animal/hostile/abolition_extremist/has_ranged_attack() + return TRUE + +/datum/mob_controller/abolitionist + speak_chance = 0 + turns_per_wander = 10 + stop_wander_when_pulled = 0 + can_escape_buckles = TRUE + /mob/living/simple_animal/hostile/abolition_extremist/death(gibbed) . = ..() if(. && !gibbed) diff --git a/maps/away/smugglers/smugglers.dmm b/maps/away/smugglers/smugglers.dmm index 9de3527ba05..f7607882fb3 100644 --- a/maps/away/smugglers/smugglers.dmm +++ b/maps/away/smugglers/smugglers.dmm @@ -711,7 +711,7 @@ /turf/floor, /area/smugglers/base) "bP" = ( -/mob/living/simple_animal/hostile/retaliate/malf_drone, +/mob/living/simple_animal/hostile/malf_drone, /turf/floor/tiled, /area/smugglers/office) "bQ" = ( @@ -923,7 +923,7 @@ "cz" = ( /obj/structure/bed, /obj/effect/decal/cleanable/dirt, -/mob/living/simple_animal/hostile/retaliate/malf_drone, +/mob/living/simple_animal/hostile/malf_drone, /turf/floor/tiled, /area/smugglers/dorms) "cA" = ( diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index 6e156d985a6..070692cefed 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -45667,7 +45667,7 @@ /turf/floor/tiled/white, /area/exodus/crew_quarters/heads/cmo) "bRd" = ( -/mob/living/simple_animal/cat/fluff/runtime, +/mob/living/simple_animal/passive/cat/fluff/runtime, /turf/floor/tiled/white, /area/exodus/crew_quarters/heads/cmo) "bRe" = ( @@ -63855,7 +63855,7 @@ /obj/structure/cable/green{ icon_state = "4-8" }, -/mob/living/simple_animal/hostile/retaliate/parrot/Poly, +/mob/living/simple_animal/hostile/parrot/Poly, /turf/floor/tiled/steel_grid, /area/exodus/crew_quarters/heads/chief) "kxa" = ( diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index 0338f10a82e..147d50c7715 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -11926,7 +11926,7 @@ /turf/floor/tiled, /area/ministation/engine) "RY" = ( -/mob/living/simple_animal/hostile/retaliate/parrot/Poly, +/mob/living/simple_animal/hostile/parrot/Poly, /obj/structure/table/reinforced, /obj/item/chems/drinks/glass2/coffeecup/one, /turf/floor/wood/yew, diff --git a/maps/ministation/ministation-1.dmm b/maps/ministation/ministation-1.dmm index 1676b69536a..b305b2677ba 100644 --- a/maps/ministation/ministation-1.dmm +++ b/maps/ministation/ministation-1.dmm @@ -4972,10 +4972,7 @@ /obj/effect/floor_decal/corner/paleblue{ dir = 6 }, -/mob/living/simple_animal/crow{ - desc = "She's not a real doctor but she is a real bird."; - name = "Dr. Bird"; - }, +/mob/living/simple_animal/crow/doctor, /turf/floor/tiled/white, /area/ministation/medical) "vK" = ( diff --git a/maps/ministation/ministation-2.dmm b/maps/ministation/ministation-2.dmm index f934e96902b..8dd3b7c3fd3 100644 --- a/maps/ministation/ministation-2.dmm +++ b/maps/ministation/ministation-2.dmm @@ -2474,7 +2474,7 @@ /turf/floor/tiled/white, /area/ministation/science) "kz" = ( -/mob/living/simple_animal/cat/fluff/ran, +/mob/living/simple_animal/passive/cat/fluff/ran, /turf/floor/tiled/white, /area/ministation/science) "kG" = ( diff --git a/maps/ministation/ministation_objects.dm b/maps/ministation/ministation_objects.dm index 1ff4956772d..9b76efc1c3b 100644 --- a/maps/ministation/ministation_objects.dm +++ b/maps/ministation/ministation_objects.dm @@ -71,4 +71,8 @@ initial_access = list(access_bridge) /obj/machinery/camera/network/hallway - preset_channels = list("Hallway") \ No newline at end of file + preset_channels = list("Hallway") + +/mob/living/simple_animal/crow/doctor + desc = "She's not a real doctor, but she is a real bird." + name = "Dr. Bird" diff --git a/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dm b/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dm index 6eb53c85843..20a52f41185 100644 --- a/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dm +++ b/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dm @@ -81,7 +81,7 @@ // Mobs // -/mob/living/simple_animal/hostile/retaliate/goat/hydro +/mob/living/simple_animal/hostile/goat/hydro name = "goat" desc = "An impressive goat, in size and coat. His horns look pretty serious!" max_health = 100 @@ -91,26 +91,31 @@ /obj/item/natural_weapon/hooves/strong force = 15 -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro +/mob/living/simple_animal/hostile/malf_drone/hydro name = "Farmbot" desc = "The botanist's best friend. There's something slightly odd about the way it moves." icon = 'maps/random_ruins/exoplanet_ruins/hydrobase/farmbot.dmi' - emote_speech = list("Initiating harvesting subrout-ine-ine.", "Connection timed out.", "Connection with master AI syst-tem-tem lost.", "Core systems override enab-...") - emote_see = list("beeps repeatedly", "whirrs violently", "flashes its indicator lights", "emits a ping sound") faction = "farmbots" max_health = 225 + ai = /datum/mob_controller/aggressive/malf_drone/hydro + +/datum/mob_controller/aggressive/malf_drone/hydro malfunctioning = 0 + emote_speech = list("Initiating harvesting subrout-ine-ine.", "Connection timed out.", "Connection with master AI syst-tem-tem lost.", "Core systems override enab-...") + emote_see = list("beeps repeatedly", "whirrs violently", "flashes its indicator lights", "emits a ping sound") -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro/Initialize() +/mob/living/simple_animal/hostile/malf_drone/hydro/Initialize() . = ..() if(prob(15)) projectiletype = /obj/item/projectile/beam/drone/weak -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro/emp_act(severity) +/mob/living/simple_animal/hostile/malf_drone/hydro/emp_act(severity) take_damage(rand(5, 10) * (severity + 1)) disabled = rand(15, 30) - malfunctioning = 1 - hostile_drone = 1 - destroy_surroundings = 1 + var/datum/mob_controller/aggressive/malf_drone/drone_brain = ai + if(istype(drone_brain)) + drone_brain.malfunctioning = 1 + drone_brain.hostile_drone = 1 + drone_brain.try_destroy_surroundings = TRUE projectiletype = initial(projectiletype) stop_automove() diff --git a/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dmm b/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dmm index 5fbdbd3fbef..384c9603cb9 100644 --- a/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dmm +++ b/maps/random_ruins/exoplanet_ruins/hydrobase/hydrobase.dmm @@ -177,7 +177,7 @@ /area/map_template/hydrobase/station/growD) "aD" = ( /obj/machinery/mech_recharger, -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro, +/mob/living/simple_animal/hostile/malf_drone/hydro, /turf/floor/bluegrid, /area/map_template/hydrobase/station/growD) "aE" = ( @@ -287,7 +287,7 @@ dir = 4; pixel_x = -24 }, -/mob/living/simple_animal/hostile/retaliate/goat/hydro, +/mob/living/simple_animal/hostile/goat/hydro, /turf/floor/tiled/dark, /area/map_template/hydrobase/station/goatzone) "aV" = ( @@ -367,7 +367,7 @@ /obj/structure/cable/yellow{ icon_state = "4-8" }, -/mob/living/simple_animal/hostile/retaliate/goat/hydro, +/mob/living/simple_animal/hostile/goat/hydro, /turf/floor/tiled/dark, /area/map_template/hydrobase/station/goatzone) "be" = ( @@ -417,7 +417,7 @@ dir = 4 }, /obj/machinery/light, -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro, +/mob/living/simple_animal/hostile/malf_drone/hydro, /turf/floor/bluegrid, /area/map_template/hydrobase/station/growA) "bl" = ( @@ -591,7 +591,7 @@ dir = 1; icon_state = "tube1" }, -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro, +/mob/living/simple_animal/hostile/malf_drone/hydro, /turf/floor/bluegrid, /area/map_template/hydrobase/station/growA) "bG" = ( @@ -744,7 +744,7 @@ pixel_x = 25 }, /obj/machinery/mech_recharger, -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro, +/mob/living/simple_animal/hostile/malf_drone/hydro, /turf/floor/tiled/white, /area/map_template/hydrobase/station/growD) "cd" = ( @@ -913,7 +913,7 @@ pixel_y = 4 }, /obj/machinery/mech_recharger, -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro, +/mob/living/simple_animal/hostile/malf_drone/hydro, /turf/floor/bluegrid, /area/map_template/hydrobase/station/growC) "cD" = ( @@ -1515,7 +1515,7 @@ /area/map_template/hydrobase/station/growC) "ee" = ( /obj/machinery/mech_recharger, -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro, +/mob/living/simple_animal/hostile/malf_drone/hydro, /turf/floor/bluegrid, /area/map_template/hydrobase/station/growC) "ef" = ( @@ -1828,7 +1828,7 @@ /area/map_template/hydrobase/station/growB) "eO" = ( /obj/machinery/mech_recharger, -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro, +/mob/living/simple_animal/hostile/malf_drone/hydro, /turf/floor/bluegrid, /area/map_template/hydrobase/station/growB) "eP" = ( @@ -1856,7 +1856,7 @@ pixel_x = 25 }, /obj/machinery/mech_recharger, -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro, +/mob/living/simple_animal/hostile/malf_drone/hydro, /turf/floor/bluegrid, /area/map_template/hydrobase/station/growB) "eS" = ( @@ -2150,7 +2150,7 @@ "fA" = ( /obj/machinery/light, /obj/machinery/mech_recharger, -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro, +/mob/living/simple_animal/hostile/malf_drone/hydro, /turf/floor/bluegrid, /area/map_template/hydrobase/station/growA) "fB" = ( @@ -2317,7 +2317,7 @@ dir = 1; icon_state = "tube1" }, -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro, +/mob/living/simple_animal/hostile/malf_drone/hydro, /turf/floor/bluegrid, /area/map_template/hydrobase/station/growA) "fW" = ( @@ -2518,7 +2518,7 @@ /area/map_template/hydrobase/station/growB) "gt" = ( /obj/machinery/mech_recharger, -/mob/living/simple_animal/hostile/retaliate/malf_drone/hydro, +/mob/living/simple_animal/hostile/malf_drone/hydro, /turf/floor/tiled/white, /area/map_template/hydrobase/station/growB) "gu" = ( @@ -2697,7 +2697,7 @@ /turf/template_noop, /area/template_noop) "NL" = ( -/mob/living/simple_animal/hostile/retaliate/goat/hydro, +/mob/living/simple_animal/hostile/goat/hydro, /turf/floor/tiled/dark, /area/map_template/hydrobase/station/goatzone) "RI" = ( diff --git a/maps/shaded_hills/areas/woods.dm b/maps/shaded_hills/areas/woods.dm index fdaec01adc1..1aaeac6b4ac 100644 --- a/maps/shaded_hills/areas/woods.dm +++ b/maps/shaded_hills/areas/woods.dm @@ -8,7 +8,7 @@ /mob/living/simple_animal/aquatic/fish/large/bass = 5, /mob/living/simple_animal/aquatic/fish/large/trout = 5, /mob/living/simple_animal/aquatic/fish/large/javelin = 5, - /mob/living/simple_animal/hostile/retaliate/aquatic/carp = 3, + /mob/living/simple_animal/hostile/aquatic/carp = 3, /mob/living/simple_animal/aquatic/fish/large/koi = 1 ) diff --git a/maps/shaded_hills/shaded_hills-dungeon.dmm b/maps/shaded_hills/shaded_hills-dungeon.dmm index e99b4bced33..2eab5c6e508 100644 --- a/maps/shaded_hills/shaded_hills-dungeon.dmm +++ b/maps/shaded_hills/shaded_hills-dungeon.dmm @@ -57,7 +57,7 @@ /turf/floor/natural/rock/basalt, /area/shaded_hills/caves/dungeon/poi) "Xx" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/caves/dungeon) "YN" = ( /turf/floor/natural/path/running_bond/basalt, diff --git a/maps/shaded_hills/shaded_hills-grassland.dmm b/maps/shaded_hills/shaded_hills-grassland.dmm index ce7adf9a2eb..4382259e5ba 100644 --- a/maps/shaded_hills/shaded_hills-grassland.dmm +++ b/maps/shaded_hills/shaded_hills-grassland.dmm @@ -15,7 +15,7 @@ /turf/floor/natural/grass, /area/shaded_hills/outside) "dB" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/caves/unexplored) "ee" = ( /turf/floor/natural/path/running_bond/basalt, @@ -55,7 +55,7 @@ /area/shaded_hills/outside/river) "iP" = ( /obj/structure/fire_source/firepit/basalt, -/obj/item/stack/material/log/mapped/ebony/ten, +/obj/item/stack/material/log/mapped/walnut/ten, /turf/floor/natural/barren, /area/shaded_hills/outside) "jj" = ( @@ -111,9 +111,9 @@ /turf/floor/natural/barren, /area/shaded_hills/caves/entrance) "nl" = ( -/obj/structure/door/wood/ebony, +/obj/structure/door/walnut, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside) "oo" = ( /obj/item/stack/material/ore/handful/sand, @@ -147,7 +147,7 @@ /area/shaded_hills/caves/unexplored) "ul" = ( /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside) "vX" = ( /turf/floor/natural/path/running_bond/basalt, @@ -162,7 +162,7 @@ /turf/floor/natural/dirt, /area/shaded_hills/outside) "xC" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside) "yA" = ( /obj/abstract/landmark/latejoin/observer, @@ -191,7 +191,7 @@ /turf/floor/woven, /area/shaded_hills/outside) "EE" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/river) "EL" = ( /obj/abstract/exterior_marker/inside, @@ -209,13 +209,13 @@ /area/shaded_hills/outside) "ES" = ( /obj/abstract/exterior_marker/inside, -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/outside) "EV" = ( /turf/floor/natural/mud/water/deep, /area/shaded_hills/caves/river) "Fg" = ( -/obj/structure/door/wood/ebony, +/obj/structure/door/walnut, /obj/abstract/exterior_marker/inside, /turf/floor/natural/path/basalt, /area/shaded_hills/outside) @@ -262,7 +262,7 @@ /turf/floor/natural/rock/basalt, /area/shaded_hills/outside) "JN" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/outside/river) "Kd" = ( /turf/floor/natural/mud, @@ -369,7 +369,7 @@ /turf/floor/natural/rock/basalt, /area/shaded_hills/caves/unexplored/south) "ZV" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/outside) (1,1,1) = {" diff --git a/maps/shaded_hills/shaded_hills-inn.dmm b/maps/shaded_hills/shaded_hills-inn.dmm index 97145e52ed9..596765e7b44 100644 --- a/maps/shaded_hills/shaded_hills-inn.dmm +++ b/maps/shaded_hills/shaded_hills-inn.dmm @@ -14,7 +14,7 @@ /obj/structure/bed/chair/wood/ebony{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "bz" = ( /obj/structure/closet/crate/chest, @@ -23,51 +23,51 @@ "bW" = ( /obj/structure/table/woodentable/ebony, /obj/item/chems/cooking_vessel/skillet, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "cl" = ( /obj/structure/table/woodentable/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "cq" = ( /obj/structure/table/woodentable/ebony, /obj/item/chems/glass/pottery/bowl, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "cx" = ( /turf/wall/brick/basalt, /area/shaded_hills/outside/downlands) "cy" = ( /obj/structure/reagent_dispensers/barrel/ebony/water, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "cT" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 1 }, -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, /turf/floor/natural/grass, /area/shaded_hills/outside/shrine) "dC" = ( -/obj/structure/door/wood/ebony{ +/obj/structure/door/walnut{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "dH" = ( -/turf/wall/log/ebony/shutter, +/turf/wall/log/walnut/shutter, /area/shaded_hills/general_store) "dK" = ( /obj/structure/table/woodentable/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn/porch) "dN" = ( /obj/structure/wall_sconce/lantern{ dir = 8 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "ev" = ( /obj/structure/closet/crate/chest/ebony, @@ -75,15 +75,15 @@ /area/shaded_hills/shrine) "eD" = ( /obj/structure/textiles/loom/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "eG" = ( -/obj/structure/door/wood/ebony, -/turf/floor/wood/ebony, +/obj/structure/door/walnut, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "fg" = ( -/obj/structure/railing/mapped/ebony, -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut, +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, /obj/structure/reagent_dispensers/compost_bin, @@ -113,7 +113,7 @@ /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/inn) "fR" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/tannery) "fS" = ( /obj/structure/table/woodentable/ebony, @@ -134,10 +134,10 @@ dir = 4 }, /obj/structure/table/woodentable/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "gp" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 1 }, /turf/floor/natural/grass, @@ -146,17 +146,17 @@ /obj/structure/wall_sconce/lantern{ dir = 8 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "gA" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, /turf/floor/natural/grass, /area/shaded_hills/outside/shrine) "gB" = ( /obj/structure/wall_sconce/lantern, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "gL" = ( /obj/structure/reagent_dispensers/barrel/ebony/oil, @@ -172,23 +172,23 @@ /obj/structure/bed/chair/bench/ebony{ dir = 1 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "hE" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, -/obj/structure/railing/mapped/ebony, +/obj/structure/railing/mapped/wooden/walnut, /turf/floor/natural/grass, /area/shaded_hills/outside/downlands) "hJ" = ( /obj/structure/bed/chair/bench/ebony{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "hU" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, /turf/floor/natural/grass, @@ -198,7 +198,7 @@ dir = 1; pixel_y = 10 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn/porch) "id" = ( /obj/structure/table/woodentable/ebony, @@ -214,16 +214,16 @@ /obj/structure/bed/chair/wood/ebony{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "iC" = ( -/obj/structure/door/wood/ebony{ +/obj/structure/door/walnut{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "iH" = ( -/obj/structure/door/wood/ebony, +/obj/structure/door/walnut, /obj/abstract/landmark/lock_preset/shaded_hills/inn_interior, /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/inn) @@ -231,28 +231,28 @@ /turf/wall/brick/basalt, /area/shaded_hills/outside/shrine) "iX" = ( -/turf/wall/log/ebony/shutter, +/turf/wall/log/walnut/shutter, /area/shaded_hills/inn) "iZ" = ( /obj/structure/fire_source/fireplace/basalt, -/obj/item/stack/material/log/mapped/ebony/twenty, +/obj/item/stack/material/log/mapped/walnut/twenty, /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/inn) "jf" = ( /obj/structure/wall_sconce/lantern{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/stable) "jk" = ( /obj/structure/bed/chair/bench/pew/mahogany{ dir = 1 }, /obj/abstract/landmark/start/shaded_hills/cleric, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "js" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/stable) "jx" = ( /obj/structure/reagent_dispensers/barrel/ebony, @@ -270,7 +270,7 @@ "jA" = ( /obj/structure/bed/simple/ebony, /obj/item/bedsheet/furs, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/stable) "jI" = ( /obj/structure/wall_sconce/lantern{ @@ -291,8 +291,8 @@ /turf/floor/natural/dirt, /area/shaded_hills/outside/shrine) "kE" = ( -/obj/structure/door/wood/ebony, -/turf/floor/wood/ebony, +/obj/structure/door/walnut, +/turf/floor/wood/walnut, /area/shaded_hills/stable) "lz" = ( /obj/structure/table/woodentable_reinforced/ebony, @@ -303,10 +303,10 @@ /turf/unsimulated/dark_filler, /area/shaded_hills/outside/downlands) "lD" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn/porch) "lE" = ( /obj/abstract/level_data_spawner/shaded_hills_downlands, @@ -320,7 +320,7 @@ dir = 1 }, /obj/abstract/landmark/start/shaded_hills/farmer, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "me" = ( /obj/structure/reagent_dispensers/barrel/ebony, @@ -336,13 +336,13 @@ /turf/floor/natural/dirt, /area/shaded_hills/outside/downlands) "mG" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "nn" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/stable) "nN" = ( /obj/structure/table/woodentable_reinforced/ebony, @@ -350,7 +350,7 @@ /turf/floor/natural/path/basalt, /area/shaded_hills/slaughterhouse) "ol" = ( -/obj/structure/railing/mapped/ebony, +/obj/structure/railing/mapped/wooden/walnut, /turf/floor/natural/mud, /area/shaded_hills/outside/shrine) "or" = ( @@ -360,10 +360,10 @@ /turf/floor/natural/path/basalt, /area/shaded_hills/stable) "oO" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 1 }, /turf/floor/natural/grass, @@ -372,7 +372,7 @@ /obj/structure/bed/chair/bench/pew/mahogany{ dir = 1 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "pi" = ( /obj/structure/table/woodentable/ebony, @@ -383,13 +383,13 @@ "qe" = ( /obj/structure/bed/simple/ebony, /obj/abstract/landmark/start/shaded_hills/shrine_attendant, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "qf" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn/porch) "qG" = ( /turf/wall/brick/basalt, @@ -402,7 +402,7 @@ /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/shrine) "qP" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "qY" = ( /turf/wall/brick/basalt, @@ -411,7 +411,7 @@ /obj/structure/table/woodentable/ebony, /obj/item/chems/glass/mortar, /obj/item/rock/basalt, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "rv" = ( /obj/structure/table/woodentable/ebony, @@ -423,33 +423,33 @@ /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/inn) "ry" = ( -/obj/structure/railing/mapped/ebony, -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut, +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn/porch) "rO" = ( /mob/living/simple_animal/fowl/chicken, /turf/floor/natural/grass, /area/shaded_hills/outside/downlands) "sJ" = ( -/obj/structure/door/wood/ebony, +/obj/structure/door/walnut, /obj/abstract/landmark/lock_preset/shaded_hills/inn_interior, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "sM" = ( /obj/structure/table/woodentable_reinforced/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "th" = ( /obj/structure/bed/simple/ebony/cloth, /obj/item/bedsheet/yellowed, /obj/abstract/landmark/start/shaded_hills/inn_worker, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "tq" = ( -/obj/structure/door/wood/ebony, +/obj/structure/door/walnut, /turf/floor/natural/path/basalt, /area/shaded_hills/slaughterhouse) "tF" = ( @@ -462,27 +462,27 @@ /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/inn) "tS" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, -/obj/structure/railing/mapped/ebony, +/obj/structure/railing/mapped/wooden/walnut, /turf/floor/natural/grass, /area/shaded_hills/outside/downlands) "uu" = ( /obj/structure/bed/simple/ebony, /obj/item/bedsheet/furs, /obj/abstract/landmark/start/shaded_hills/farmer, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "uA" = ( /obj/structure/wall_sconce/lantern{ dir = 1; pixel_y = 10 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "uJ" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "uK" = ( /obj/structure/table/marble, @@ -499,7 +499,7 @@ dir = 1; pixel_y = 10 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "vA" = ( /obj/structure/table/marble, @@ -543,28 +543,28 @@ /turf/wall/brick/basalt, /area/shaded_hills/slaughterhouse) "xU" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, /turf/floor/natural/grass, /area/shaded_hills/outside/shrine) "xW" = ( -/obj/structure/reagent_dispensers/compost_bin, +/obj/structure/reagent_dispensers/compost_bin/walnut, /turf/floor/natural/dirt, /area/shaded_hills/outside/downlands) "ya" = ( /obj/structure/reagent_dispensers/barrel/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "yn" = ( /obj/structure/bed/simple/ebony/cloth, /obj/item/bedsheet/yellowed, /obj/abstract/landmark/start/shaded_hills/innkeeper, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "yu" = ( /obj/structure/reagent_dispensers/barrel/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "yx" = ( /obj/structure/table/marble, @@ -578,11 +578,11 @@ dir = 1 }, /obj/abstract/landmark/start/shaded_hills/farmer, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "yN" = ( /obj/structure/closet/crate/chest/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "yY" = ( /turf/floor/natural/grass, @@ -598,10 +598,10 @@ /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/inn) "zm" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 1 }, /turf/floor/natural/grass, @@ -615,10 +615,10 @@ /area/shaded_hills/inn) "Aq" = ( /obj/structure/textiles/loom/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "Aw" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, /obj/structure/reagent_dispensers/barrel/ebony, @@ -636,19 +636,19 @@ /turf/floor/natural/mud/water, /area/shaded_hills/outside/downlands) "AE" = ( -/obj/structure/door/wood/ebony, +/obj/structure/door/walnut, /turf/floor/natural/path/basalt, /area/shaded_hills/shrine) "AG" = ( /obj/structure/table/woodentable/ebony, /obj/item/bag/sack, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "AX" = ( /obj/structure/wall_sconce/lantern{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "Br" = ( /turf/floor/natural/path/basalt, @@ -663,11 +663,11 @@ /area/shaded_hills/outside/shrine) "BD" = ( /obj/structure/reagent_dispensers/barrel/ebony/wine, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "BG" = ( /obj/structure/bed/chair/bench/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "BI" = ( /obj/abstract/map_data/shaded_hills, @@ -676,32 +676,32 @@ "BL" = ( /obj/structure/bed/simple/ebony/cloth, /obj/item/bedsheet/yellowed, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "CR" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "Dn" = ( /obj/structure/reagent_dispensers/barrel/ebony/oil, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "Dt" = ( /obj/structure/bed/simple/ebony/cloth, /obj/abstract/landmark/start/shaded_hills/shrine_keeper, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "Ee" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/inn) "Ep" = ( /obj/structure/wall_sconce/lantern{ dir = 4 }, /obj/structure/table/woodentable/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "EJ" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 1 }, /turf/floor/natural/grass, @@ -729,7 +729,7 @@ /area/shaded_hills/outside/downlands) "FE" = ( /obj/structure/closet/cabinet/wooden/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "FH" = ( /obj/structure/wall_sconce/lantern{ @@ -756,14 +756,14 @@ /turf/floor/natural/path/basalt, /area/shaded_hills/outside/downlands) "Gs" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "Gu" = ( /obj/structure/fire_source/stove, -/obj/item/stack/material/log/mapped/ebony/twenty, +/obj/item/stack/material/log/mapped/walnut/twenty, /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/inn) "Gw" = ( @@ -776,7 +776,7 @@ dir = 1; pixel_y = 10 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "GW" = ( /obj/machinery/portable_atmospherics/hydroponics/soil, @@ -793,11 +793,11 @@ /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/inn) "HE" = ( -/turf/wall/log/ebony/shutter, +/turf/wall/log/walnut/shutter, /area/shaded_hills/stable) "HF" = ( /obj/structure/textiles/spinning_wheel/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "HI" = ( /turf/floor/natural/grass, @@ -806,29 +806,29 @@ /turf/floor/natural/path/running_bond/basalt, /area/shaded_hills/outside/downlands) "Il" = ( -/obj/structure/door/wood/ebony{ +/obj/structure/door/walnut{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "Im" = ( /obj/structure/wall_sconce/lantern{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "Iz" = ( /obj/abstract/exterior_marker/inside, /turf/wall/brick/basalt, /area/shaded_hills/outside/downlands) "IL" = ( -/obj/structure/door/wood/ebony{ +/obj/structure/door/walnut{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/tannery) "IQ" = ( -/turf/wall/log/ebony/shutter, +/turf/wall/log/walnut/shutter, /area/shaded_hills/farmhouse) "IS" = ( /turf/floor/natural/mud/water/deep, @@ -844,26 +844,26 @@ /area/shaded_hills/outside/shrine) "Jo" = ( /obj/structure/wall_sconce/lantern, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "Jp" = ( /obj/abstract/landmark/lock_preset/shaded_hills/inn_interior, -/obj/structure/door/wood/ebony{ +/obj/structure/door/walnut{ dir = 4 }, /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/inn) "Jt" = ( /obj/structure/closet/cabinet/wooden/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "Jw" = ( /obj/structure/bed/chair/wood/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn/porch) "Jz" = ( /obj/structure/reagent_dispensers/barrel/ebony/water, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "JK" = ( /obj/structure/table/woodentable_reinforced/mahogany, @@ -877,8 +877,8 @@ /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/shrine) "JU" = ( -/obj/structure/railing/mapped/ebony, -/turf/floor/wood/ebony, +/obj/structure/railing/mapped/wooden/walnut, +/turf/floor/wood/walnut, /area/shaded_hills/inn/porch) "Ka" = ( /mob/living/simple_animal/cow, @@ -889,10 +889,10 @@ /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/shrine) "Kh" = ( -/turf/wall/log/ebony/shutter, +/turf/wall/log/walnut/shutter, /area/shaded_hills/shrine) "KG" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, /turf/floor/natural/path/basalt, @@ -903,16 +903,16 @@ /turf/floor/natural/dirt, /area/shaded_hills/outside/downlands) "LK" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 1 }, /turf/floor/natural/grass, /area/shaded_hills/outside/downlands) "LN" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/farmhouse) "Mb" = ( /obj/abstract/landmark/start/shaded_hills/traveller/learned, @@ -939,7 +939,7 @@ "Ot" = ( /obj/structure/table/woodentable/ebony, /obj/item/chems/cooking_vessel/pot, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "ON" = ( /obj/structure/table/marble, @@ -961,8 +961,8 @@ /turf/floor/straw, /area/shaded_hills/stable) "PG" = ( -/obj/structure/door/wood/ebony, -/turf/floor/wood/ebony, +/obj/structure/door/walnut, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "PQ" = ( /obj/structure/closet/crate/chest, @@ -973,33 +973,33 @@ /turf/floor/natural/path/basalt, /area/shaded_hills/slaughterhouse) "Qs" = ( -/obj/structure/door/wood/ebony, +/obj/structure/door/walnut, /obj/abstract/landmark/lock_preset/shaded_hills/inn_exterior, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "QB" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/shrine) "QL" = ( /obj/structure/bed/chair/wood/ebony{ dir = 1 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "QQ" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/tannery) "QS" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "Rl" = ( /turf/floor/natural/mud, /area/shaded_hills/outside/downlands) "RC" = ( -/obj/structure/railing/mapped/ebony, +/obj/structure/railing/mapped/wooden/walnut, /turf/floor/natural/grass, /area/shaded_hills/outside/downlands) "RG" = ( @@ -1012,13 +1012,13 @@ /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/inn) "Sf" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, /turf/floor/natural/grass, /area/shaded_hills/outside/downlands) "Sj" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, /obj/item/chems/glass/bucket/wood, @@ -1026,14 +1026,14 @@ /area/shaded_hills/outside/shrine) "Sy" = ( /obj/structure/textiles/spinning_wheel/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "SF" = ( -/obj/structure/door/wood/ebony, +/obj/structure/door/walnut, /turf/floor/natural/path/basalt, /area/shaded_hills/general_store) "SG" = ( -/obj/structure/door/wood/ebony, +/obj/structure/door/walnut, /turf/floor/natural/path/basalt, /area/shaded_hills/stable) "SW" = ( @@ -1053,17 +1053,17 @@ /obj/structure/bed/chair/wood/ebony{ dir = 8 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "Ti" = ( -/obj/structure/door/wood/ebony{ +/obj/structure/door/walnut{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "Tr" = ( /obj/structure/table/woodentable_reinforced/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "Ts" = ( /obj/structure/wall_sconce/lantern, @@ -1080,28 +1080,28 @@ /obj/item/chems/food/grown/carrot, /obj/item/chems/food/grown/carrot, /obj/item/chems/food/grown/cabbage, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "TR" = ( /turf/floor/natural/dirt, /area/shaded_hills/outside/downlands) "TZ" = ( -/obj/structure/door/wood/ebony, +/obj/structure/door/walnut, /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/shrine) "UD" = ( /obj/structure/table/woodentable/ebony, /obj/item/flame/candle, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "US" = ( /obj/structure/bed/chair/wood/ebony{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "UU" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, /turf/floor/natural/path/basalt, @@ -1111,19 +1111,19 @@ /area/shaded_hills/shrine) "VE" = ( /obj/structure/reagent_dispensers/barrel/ebony/beer, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "VM" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, /turf/floor/natural/dirt, /area/shaded_hills/outside/shrine) "VU" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "VW" = ( /obj/structure/table/marble, @@ -1134,16 +1134,16 @@ /turf/floor/natural/dirt, /area/shaded_hills/outside/downlands/poi) "Wg" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/general_store) "Wh" = ( /obj/structure/table/woodentable/ebony, /obj/item/chems/glass/mortar, /obj/item/rock/basalt, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/shrine) "Wk" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, /turf/floor/straw, @@ -1151,17 +1151,17 @@ "Wl" = ( /obj/structure/table/woodentable_reinforced/ebony, /obj/item/chems/glass/pottery/cup, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "Wm" = ( /turf/unsimulated/mask, /area/shaded_hills/outside/downlands) "WD" = ( -/obj/structure/railing/mapped/ebony, -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut, +/obj/structure/railing/mapped/wooden/walnut{ dir = 8 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn/porch) "WE" = ( /obj/structure/reagent_dispensers/barrel/ebony, @@ -1175,7 +1175,7 @@ /obj/item/seeds/extracted/rice, /obj/item/seeds/extracted/potato, /obj/item/seeds/extracted/potato, -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 4 }, /turf/floor/natural/mud, @@ -1184,11 +1184,11 @@ /obj/structure/wall_sconce/lantern{ dir = 8 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "WO" = ( -/obj/structure/door/wood/ebony, -/turf/floor/wood/ebony, +/obj/structure/door/walnut, +/turf/floor/wood/walnut, /area/shaded_hills/farmhouse) "WV" = ( /obj/structure/table/marble, @@ -1199,7 +1199,7 @@ "WY" = ( /obj/structure/table/woodentable/ebony, /obj/structure/wall_sconce/lantern, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) "Xr" = ( /obj/structure/meat_hook, @@ -1207,7 +1207,7 @@ /area/shaded_hills/slaughterhouse) "XH" = ( /obj/structure/fire_source/stove, -/obj/item/stack/material/log/mapped/ebony/twenty, +/obj/item/stack/material/log/mapped/walnut/twenty, /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/shrine) "XM" = ( @@ -1220,7 +1220,7 @@ /turf/wall/brick/basalt, /area/shaded_hills/shrine) "Yg" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn/porch) "Yp" = ( /obj/structure/meat_hook, @@ -1235,7 +1235,7 @@ /obj/structure/wall_sconce/lantern{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/general_store) "Zx" = ( /obj/machinery/portable_atmospherics/hydroponics/soil, @@ -1247,7 +1247,7 @@ /turf/floor/natural/path/herringbone/basalt, /area/shaded_hills/inn) "ZY" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/inn) (1,1,1) = {" diff --git a/maps/shaded_hills/shaded_hills-swamp.dmm b/maps/shaded_hills/shaded_hills-swamp.dmm index 4711bd01fab..e931c0f6b8b 100644 --- a/maps/shaded_hills/shaded_hills-swamp.dmm +++ b/maps/shaded_hills/shaded_hills-swamp.dmm @@ -1,9 +1,9 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "aM" = ( -/turf/wall/log/ebony/shutter, +/turf/wall/log/walnut/shutter, /area/shaded_hills/witch_hut) "bg" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) "cE" = ( /turf/floor/natural/mud/water, @@ -12,7 +12,7 @@ /obj/structure/bed/chair/wood/ebony{ dir = 8 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) "hT" = ( /turf/floor/natural/mud/water/deep, @@ -40,7 +40,7 @@ /obj/structure/closet/crate/chest/ebony, /obj/item/rock/hematite, /obj/item/rock/flint, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) "oF" = ( /turf/unsimulated/dark_filler, @@ -50,7 +50,7 @@ /area/shaded_hills/caves/swamp) "pl" = ( /obj/structure/drying_rack, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) "rP" = ( /obj/structure/reagent_dispensers/barrel/ebony/water, @@ -70,7 +70,7 @@ /obj/structure/wall_sconce/lantern{ dir = 8 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) "vZ" = ( /turf/floor/natural/mud/water, @@ -91,7 +91,7 @@ /turf/floor/natural/mud, /area/shaded_hills/outside/river/swamp) "BF" = ( -/obj/structure/door/ebony, +/obj/structure/door/walnut, /turf/floor/natural/path/basalt, /area/shaded_hills/witch_hut) "BY" = ( @@ -102,7 +102,7 @@ /turf/unsimulated/dark_filler, /area/shaded_hills/caves/unexplored/swamp) "Ev" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/witch_hut) "EI" = ( /turf/unsimulated/mask, @@ -113,7 +113,7 @@ /obj/item/chems/glass/pottery/cup, /obj/item/chems/glass/pottery/cup, /obj/item/chems/glass/bucket/wood, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) "Ic" = ( /turf/floor/natural/clay, @@ -132,11 +132,11 @@ /obj/item/seeds/extracted/yarrow, /obj/item/seeds/extracted/yarrow, /obj/item/seeds/extracted/yarrow, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) "KA" = ( /obj/abstract/landmark/start/shaded_hills/herbalist, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) "LF" = ( /turf/wall/natural/basalt/shaded_hills, @@ -149,8 +149,8 @@ /turf/floor/natural/mud, /area/shaded_hills/outside/swamp) "SE" = ( -/obj/structure/door/ebony, -/turf/floor/wood/ebony, +/obj/structure/door/walnut, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) "Ty" = ( /obj/abstract/landmark/start/shaded_hills/traveller, @@ -164,7 +164,7 @@ /area/shaded_hills/outside/swamp) "TR" = ( /obj/structure/closet/crate/chest/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) "Vl" = ( /obj/structure/table/woodentable/ebony, @@ -173,7 +173,7 @@ }, /obj/item/chems/glass/mortar, /obj/item/rock/basalt, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) "VA" = ( /turf/wall/brick/basalt, @@ -191,7 +191,7 @@ /obj/structure/bed/simple/ebony, /obj/item/bedsheet/furs, /obj/abstract/landmark/start/shaded_hills/herbalist, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/witch_hut) (1,1,1) = {" diff --git a/maps/shaded_hills/shaded_hills-woods.dmm b/maps/shaded_hills/shaded_hills-woods.dmm index b376ba3d5af..3f266a6434c 100644 --- a/maps/shaded_hills/shaded_hills-woods.dmm +++ b/maps/shaded_hills/shaded_hills-woods.dmm @@ -10,10 +10,10 @@ "cN" = ( /obj/structure/table/woodentable/ebony, /obj/item/bladed/knife, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/forester_hut) "dp" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/outside/river/woods) "eJ" = ( /obj/abstract/landmark/start/shaded_hills/forester, @@ -42,7 +42,7 @@ /turf/wall/natural/basalt/shaded_hills, /area/shaded_hills/caves/river/woods) "lb" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/forester_hut) "lC" = ( /turf/unsimulated/dark_filler, @@ -54,7 +54,7 @@ /turf/floor/natural/mud/water, /area/shaded_hills/outside/river/lake) "mo" = ( -/obj/structure/door/ebony, +/obj/structure/door/walnut, /turf/floor/natural/path/basalt, /area/shaded_hills/forester_hut) "nl" = ( @@ -81,12 +81,12 @@ "vw" = ( /obj/structure/table/woodentable/ebony, /obj/item/stack/material/bundle/grass/dry, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/forester_hut) "wD" = ( /obj/structure/table/woodentable/ebony, /obj/item/fishing_rod, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/forester_hut) "xn" = ( /turf/unsimulated/mask, @@ -104,8 +104,8 @@ /turf/unsimulated/dark_filler, /area/shaded_hills/caves/unexplored/woods) "AN" = ( -/obj/structure/railing/mapped/ebony, -/turf/floor/wood/ebony, +/obj/structure/railing/mapped/wooden/walnut, +/turf/floor/wood/walnut, /area/shaded_hills/outside/river/woods) "Co" = ( /obj/abstract/landmark/start/shaded_hills/traveller/learned, @@ -129,8 +129,8 @@ /area/shaded_hills/caves/woods) "Fz" = ( /obj/structure/fire_source/stove, -/obj/item/stack/material/log/mapped/ebony/ten, -/turf/floor/wood/ebony, +/obj/item/stack/material/log/mapped/walnut/ten, +/turf/floor/wood/walnut, /area/shaded_hills/forester_hut) "HA" = ( /turf/floor/natural/grass, @@ -142,17 +142,17 @@ /obj/structure/bed/simple/ebony, /obj/item/bedsheet/furs, /obj/abstract/landmark/start/shaded_hills/forester, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/forester_hut) "JI" = ( -/obj/structure/railing/mapped/ebony{ +/obj/structure/railing/mapped/wooden/walnut{ dir = 1 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/river/woods) "JJ" = ( /obj/structure/meat_hook, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/forester_hut) "LJ" = ( /turf/floor/natural/path/running_bond/basalt, @@ -162,24 +162,24 @@ /area/shaded_hills/outside/river/lake) "MR" = ( /obj/structure/wall_sconce/lantern, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/forester_hut) "MU" = ( -/turf/wall/log/ebony/shutter, +/turf/wall/log/walnut/shutter, /area/shaded_hills/forester_hut) "Oi" = ( /obj/structure/closet/crate/chest/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/forester_hut) "PO" = ( /turf/floor/natural/mud/water/deep, /area/shaded_hills/outside/river/woods) "SI" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/forester_hut) "TP" = ( /obj/structure/table/woodentable/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/forester_hut) "VA" = ( /obj/abstract/landmark/start/shaded_hills/traveller, diff --git a/maps/shaded_hills/submaps/woods/hunter_camp/hunter_camp.dmm b/maps/shaded_hills/submaps/woods/hunter_camp/hunter_camp.dmm index b13b7d1214f..38cf8b1e79a 100644 --- a/maps/shaded_hills/submaps/woods/hunter_camp/hunter_camp.dmm +++ b/maps/shaded_hills/submaps/woods/hunter_camp/hunter_camp.dmm @@ -34,7 +34,7 @@ /area/shaded_hills/outside/point_of_interest/hunter_camp) "A" = ( /obj/abstract/exterior_marker/inside, -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/outside/point_of_interest/hunter_camp) "B" = ( /obj/structure/flora/stump/tree/ebony, @@ -77,7 +77,7 @@ /area/shaded_hills/outside/point_of_interest/hunter_camp) "S" = ( /obj/structure/fire_source/firepit/basalt, -/obj/item/stack/material/log/mapped/ebony/fifteen, +/obj/item/stack/material/log/mapped/walnut/fifteen, /turf/floor/natural/barren, /area/shaded_hills/outside/point_of_interest/hunter_camp) "U" = ( diff --git a/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm b/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm index 96d949d2540..e71a303ca35 100644 --- a/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm +++ b/maps/shaded_hills/submaps/woods/old_cabin/old_cabin.dmm @@ -1,8 +1,8 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/obj/structure/door/ebony, +/obj/structure/door/walnut, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "h" = ( /turf/template_noop, @@ -13,17 +13,17 @@ "k" = ( /obj/structure/closet/crate/chest/ebony, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "o" = ( /obj/structure/drying_rack/ebony, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "u" = ( /obj/structure/reagent_dispensers/barrel/ebony/beer, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "x" = ( /obj/structure/table/woodentable/ebony, @@ -32,7 +32,7 @@ /obj/item/rock/flint, /obj/item/rock/hematite, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "z" = ( /obj/effect/spider/spiderling/mundane, @@ -41,19 +41,19 @@ dir = 1 }, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "B" = ( /obj/effect/decal/cleanable/blood, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "D" = ( /obj/effect/spider/spiderling/mundane, /obj/structure/table/woodentable/ebony, /obj/item/chems/food/grown/carrot, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "H" = ( /obj/structure/table/woodentable/ebony, @@ -62,25 +62,25 @@ /obj/item/chems/food/grown/potato, /obj/item/chems/food/grown/carrot, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "I" = ( /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "L" = ( /obj/effect/spider/spiderling/mundane, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "M" = ( /obj/abstract/exterior_marker/inside, -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "N" = ( /obj/structure/coatrack, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) "S" = ( /obj/structure/wall_sconce/torch{ @@ -95,7 +95,7 @@ /obj/effect/decal/cleanable/blood, /obj/random/jewelry, /obj/abstract/exterior_marker/inside, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/shaded_hills/outside/point_of_interest/old_cabin) (1,1,1) = {" diff --git a/maps/shaded_hills/submaps/woods/suspicious_cabin/suspicious_cabin.dmm b/maps/shaded_hills/submaps/woods/suspicious_cabin/suspicious_cabin.dmm index 2032c706884..c9bc5e31afe 100644 --- a/maps/shaded_hills/submaps/woods/suspicious_cabin/suspicious_cabin.dmm +++ b/maps/shaded_hills/submaps/woods/suspicious_cabin/suspicious_cabin.dmm @@ -6,18 +6,18 @@ /obj/structure/bed/chair/wood/ebony{ dir = 1 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/template_noop) "e" = ( /obj/structure/bed/simple/ebony/cloth, /obj/item/bedsheet/furs, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/template_noop) "f" = ( /turf/floor/natural/dirt, /area/template_noop) "g" = ( -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/template_noop) "i" = ( /obj/structure/reagent_dispensers/barrel/ebony/wine, @@ -25,18 +25,18 @@ /area/template_noop) "k" = ( /obj/structure/coatrack, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/template_noop) "q" = ( /obj/structure/table/woodentable/ebony, /obj/item/chems/glass/pottery/cup, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/template_noop) "r" = ( /obj/structure/bed/chair/bench/ebony{ dir = 4 }, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/template_noop) "s" = ( /turf/floor/natural/path/basalt, @@ -47,17 +47,15 @@ /turf/floor/natural/path/basalt, /area/template_noop) "v" = ( -/obj/structure/window/basic/full, -/obj/structure/wall_frame/log/ebony, -/turf/floor/natural/path/basalt, +/turf/wall/log/walnut/shutter, /area/template_noop) "w" = ( /obj/structure/bed/chair/wood/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/template_noop) "x" = ( -/obj/structure/door/ebony, -/turf/floor/wood/ebony, +/obj/structure/door/walnut, +/turf/floor/wood/walnut, /area/template_noop) "y" = ( /obj/structure/reagent_dispensers/barrel/ebony, @@ -65,15 +63,15 @@ /area/template_noop) "A" = ( /obj/structure/closet/crate/chest/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/template_noop) "E" = ( /obj/structure/table/woodentable/ebony, /obj/item/chems/glass/pottery/teapot, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/template_noop) "G" = ( -/obj/item/stack/material/log/mapped/ebony/twenty, +/obj/item/stack/material/log/mapped/walnut/twenty, /turf/floor/natural/path/basalt, /area/template_noop) "J" = ( @@ -82,8 +80,11 @@ /obj/item/chems/cooking_vessel/pot, /turf/floor/natural/path/basalt, /area/template_noop) +"K" = ( +/turf/wall/brick/basalt/shutter, +/area/template_noop) "M" = ( -/obj/structure/door/ebony, +/obj/structure/door/walnut, /turf/floor/natural/path, /area/template_noop) "N" = ( @@ -97,7 +98,7 @@ /area/template_noop) "P" = ( /obj/structure/bed/chair/bench/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/template_noop) "Q" = ( /obj/structure/reagent_dispensers/barrel/ebony/beer, @@ -105,7 +106,7 @@ /area/template_noop) "R" = ( /obj/structure/table/woodentable/ebony, -/turf/floor/wood/ebony, +/turf/floor/wood/walnut, /area/template_noop) "S" = ( /obj/structure/table/woodentable/ebony, @@ -122,7 +123,7 @@ /turf/wall/brick/basalt, /area/template_noop) "Y" = ( -/turf/wall/log/ebony, +/turf/wall/log/walnut, /area/template_noop) (1,1,1) = {" @@ -149,7 +150,7 @@ f X X X -X +K X X X diff --git a/maps/tradeship/tradeship-1.dmm b/maps/tradeship/tradeship-1.dmm index fcfc08e82f6..b28e804a66e 100644 --- a/maps/tradeship/tradeship-1.dmm +++ b/maps/tradeship/tradeship-1.dmm @@ -3094,7 +3094,7 @@ /obj/structure/cable{ icon_state = "1-2" }, -/mob/living/simple_animal/cat/fluff/ran, +/mob/living/simple_animal/passive/cat/fluff/ran, /turf/floor/tiled/white, /area/ship/trade/science/fabricaton) "Qg" = ( diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index 7dd27ebd071..f3134a1184a 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -14,6 +14,9 @@ #include "../random_ruins/exoplanet_ruins/playablecolony/playablecolony.dm" + #include "../../mods/content/government/away_sites/icarus/icarus.dm" + #include "../../mods/content/corporate/away_sites/lar_maria/lar_maria.dm" + #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" #include "../../mods/content/mundane.dm" #include "../../mods/content/bigpharma/_bigpharma.dme" diff --git a/mods/content/corporate/away_sites/lar_maria/lar_maria.dm b/mods/content/corporate/away_sites/lar_maria/lar_maria.dm index efc97e0ce66..e89b27c4254 100644 --- a/mods/content/corporate/away_sites/lar_maria/lar_maria.dm +++ b/mods/content/corporate/away_sites/lar_maria/lar_maria.dm @@ -29,19 +29,22 @@ environment_smash = 1 faction = "lar_maria" status_flags = CANPUSH - speak_chance = 25 - emote_speech = list("Die!", "Fresh meat!", "Hurr!", "You said help will come!", "I did nothing!", "Eat my fist!", "One for the road!") - emote_hear = list("roars", "giggles", "breathes loudly", "mumbles", "yells something unintelligible") - emote_see = list("cries", "grins insanely", "itches fiercly", "scratches his face", "shakes his fists above his head") - turns_per_wander = 5 base_movement_delay = 8 - can_escape = TRUE - stop_wandering_when_pulled = FALSE natural_weapon = /obj/item/natural_weapon/punch + ai = /datum/mob_controller/aggressive/lar_maria var/obj/abstract/landmark/corpse/lar_maria/corpse = null var/weapon = null +/datum/mob_controller/aggressive/lar_maria + emote_speech = list("Die!", "Fresh meat!", "Hurr!", "You said help will come!", "I did nothing!", "Eat my fist!", "One for the road!") + speak_chance = 12.5 + emote_hear = list("roars", "giggles", "breathes loudly", "mumbles", "yells something unintelligible") + emote_see = list("cries", "grins insanely", "itches fiercly", "scratches his face", "shakes his fists above his head") + turns_per_wander = 10 + stop_wander_when_pulled = 0 + can_escape_buckles = TRUE + /mob/living/simple_animal/hostile/lar_maria/death(gibbed) . = ..() if(. && !gibbed) @@ -111,9 +114,11 @@ /mob/living/simple_animal/hostile/lar_maria/guard/ranged weapon = /obj/item/gun/projectile/shotgun/pump - ranged = 1 projectiletype = /obj/item/projectile/bullet/shotgun/beanbag +/mob/living/simple_animal/hostile/lar_maria/guard/ranged/has_ranged_attack() + return TRUE + /obj/item/clothing/head/soft/zhp_cap name = "Zeng-Hu Pharmaceuticals cap" icon = 'mods/content/corporate/icons/clothing/head/zhp_cap.dmi' diff --git a/mods/content/fantasy/icons/clothing/braies.dmi b/mods/content/fantasy/icons/clothing/braies.dmi index d924e1d5412..dd579284e7a 100644 Binary files a/mods/content/fantasy/icons/clothing/braies.dmi and b/mods/content/fantasy/icons/clothing/braies.dmi differ diff --git a/mods/content/fantasy/icons/clothing/jerkin.dmi b/mods/content/fantasy/icons/clothing/jerkin.dmi index 2b16fee62df..bbcf50e66de 100644 Binary files a/mods/content/fantasy/icons/clothing/jerkin.dmi and b/mods/content/fantasy/icons/clothing/jerkin.dmi differ diff --git a/mods/content/fantasy/icons/clothing/loincloth.dmi b/mods/content/fantasy/icons/clothing/loincloth.dmi index 81e04c8329c..17d6766f263 100644 Binary files a/mods/content/fantasy/icons/clothing/loincloth.dmi and b/mods/content/fantasy/icons/clothing/loincloth.dmi differ diff --git a/mods/content/fantasy/icons/clothing/sack_kobaloi.dmi b/mods/content/fantasy/icons/clothing/sack_kobaloi.dmi index 4bbfa772864..8314dbb70ac 100644 Binary files a/mods/content/fantasy/icons/clothing/sack_kobaloi.dmi and b/mods/content/fantasy/icons/clothing/sack_kobaloi.dmi differ diff --git a/mods/content/fantasy/icons/clothing/trousers.dmi b/mods/content/fantasy/icons/clothing/trousers.dmi index 077a3d2c120..2e76069619a 100644 Binary files a/mods/content/fantasy/icons/clothing/trousers.dmi and b/mods/content/fantasy/icons/clothing/trousers.dmi differ diff --git a/mods/content/fantasy/icons/kobaloi/body.dmi b/mods/content/fantasy/icons/kobaloi/body.dmi index 185c1a59dc9..8a5282aa240 100644 Binary files a/mods/content/fantasy/icons/kobaloi/body.dmi and b/mods/content/fantasy/icons/kobaloi/body.dmi differ diff --git a/mods/content/fantasy/icons/kobaloi/eyes.dmi b/mods/content/fantasy/icons/kobaloi/eyes.dmi index dcf38169313..da8b3c80d3b 100644 Binary files a/mods/content/fantasy/icons/kobaloi/eyes.dmi and b/mods/content/fantasy/icons/kobaloi/eyes.dmi differ diff --git a/mods/content/fantasy/icons/kobaloi/markings.dmi b/mods/content/fantasy/icons/kobaloi/markings.dmi index 9b1fa2471db..19d4319f583 100644 Binary files a/mods/content/fantasy/icons/kobaloi/markings.dmi and b/mods/content/fantasy/icons/kobaloi/markings.dmi differ diff --git a/mods/content/matchmaking/matchmaker.dm b/mods/content/matchmaking/matchmaker.dm index 3dbf0f2ff3d..0892ba4c827 100644 --- a/mods/content/matchmaking/matchmaker.dm +++ b/mods/content/matchmaking/matchmaker.dm @@ -4,10 +4,14 @@ var/global/datum/matchmaker/matchmaker = new() matchmaker.do_matchmaking() return TRUE -/hook/player_latejoin/proc/matchmaking(var/datum/job/job, var/mob/living/character) +/datum/matchmaker/matchmaker/New() + . = ..() + events_repository.register_global(/decl/observ/player_latejoin, src, PROC_REF(matchmake_latejoiner)) + +/datum/matchmaker/proc/matchmake_latejoiner(mob/living/character, datum/job/job) if(character.mind && character.client?.prefs.relations.len) for(var/T in character.client.prefs.relations) - var/TT = matchmaker.relation_types[T] + var/TT = relation_types[T] var/datum/relation/R = new TT R.holder = character.mind R.info = character.client.prefs.relations_info[T] @@ -16,7 +20,7 @@ var/global/datum/matchmaker/matchmaker = new() return TRUE if(!job.create_record) return TRUE - matchmaker.do_matchmaking() + do_matchmaking() return TRUE /datum/mind diff --git a/mods/content/psionics/machines/psimeter.dm b/mods/content/psionics/machines/psimeter.dm index 22b1de43259..b3abc2cb9c5 100644 --- a/mods/content/psionics/machines/psimeter.dm +++ b/mods/content/psionics/machines/psimeter.dm @@ -31,9 +31,9 @@ else dat += "

TELESTO Mark I Psi-Meter


" var/found - for(var/mob/living/H in range(1, src)) + for(var/mob/living/subject in range(1, src)) found = TRUE - dat += "" dat += "
Candidates
[H.name]Conduct Assay" + dat += "
[subject.name]Conduct Assay" if(!found) dat += "
No candidates found.
" diff --git a/mods/content/xenobiology/colours/colour_gold.dm b/mods/content/xenobiology/colours/colour_gold.dm index 69b181a7f05..a6c7eeb2537 100644 --- a/mods/content/xenobiology/colours/colour_gold.dm +++ b/mods/content/xenobiology/colours/colour_gold.dm @@ -11,8 +11,8 @@ extract_icon = 'mods/content/xenobiology/icons/slimes/slime_extract_gold.dmi' reaction_strings = list(/decl/material/solid/metal/uranium = "Synthesises a cute critter.") var/list/possible_mobs = list( - /mob/living/simple_animal/cat, - /mob/living/simple_animal/cat/kitten, + /mob/living/simple_animal/passive/cat, + /mob/living/simple_animal/passive/cat/kitten, /mob/living/simple_animal/corgi, /mob/living/simple_animal/corgi/puppy, /mob/living/simple_animal/cow, diff --git a/mods/content/xenobiology/mobs/critter_slime.dm b/mods/content/xenobiology/mobs/critter_slime.dm index a27f7388f22..bbd2a5a1e46 100644 --- a/mods/content/xenobiology/mobs/critter_slime.dm +++ b/mods/content/xenobiology/mobs/critter_slime.dm @@ -5,11 +5,13 @@ speak_emote = list("chirps") max_health = 100 response_harm = "stamps on" - emote_see = list("jiggles", "bounces in place") gene_damage = -1 - + ai = /datum/mob_controller/pet_slime var/slime_type = /decl/slime_colour/grey +/datum/mob_controller/pet_slime + emote_see = list("jiggles", "bounces in place") + /mob/living/simple_animal/slime/Initialize(var/ml, var/_stype = /decl/slime_colour/grey) . = ..() slime_type = _stype diff --git a/mods/content/xenobiology/slime/slime_AI.dm b/mods/content/xenobiology/slime/slime_AI.dm index e4acd432a64..35913c42aff 100644 --- a/mods/content/xenobiology/slime/slime_AI.dm +++ b/mods/content/xenobiology/slime/slime_AI.dm @@ -1,6 +1,5 @@ /datum/mob_controller/slime expected_type = /mob/living/slime - run_interval = 1 SECOND var/mood var/chase_target = 0 var/mob/living/leader diff --git a/mods/gamemodes/cult/mobs/constructs/constructs.dm b/mods/gamemodes/cult/mobs/constructs/constructs.dm index 744b96152b1..197ae32ec58 100644 --- a/mods/gamemodes/cult/mobs/constructs/constructs.dm +++ b/mods/gamemodes/cult/mobs/constructs/constructs.dm @@ -2,9 +2,7 @@ name = "Construct" real_name = "Construct" desc = "" - emote_speech = list("Hsssssssszsht.", "Hsssssssss...", "Tcshsssssssszht!") speak_emote = list("hisses") - emote_hear = list("wails","screeches") base_animal_type = /mob/living/simple_animal/construct base_movement_delay = -1 @@ -14,7 +12,6 @@ response_harm = "punches" icon = 'icons/mob/simple_animal/shade.dmi' a_intent = I_HURT - stop_wandering = TRUE status_flags = CANPUSH universal_speak = FALSE universal_understand = TRUE @@ -32,12 +29,16 @@ gene_damage = -1 butchery_data = /decl/butchery_data/occult hud_used = /datum/hud/construct - z_flags = ZMM_MANGLE_PLANES glowing_eyes = TRUE - + ai = /datum/mob_controller/construct var/list/construct_spells = list() +/datum/mob_controller/construct + emote_speech = list("Hsssssssszsht.", "Hsssssssss...", "Tcshsssssssszht!") + emote_hear = list("wails","screeches") + do_wander = FALSE + /mob/living/simple_animal/construct/check_has_mouth() return FALSE @@ -108,9 +109,12 @@ status_flags = 0 resistance = 10 construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser) - can_escape = TRUE hud_used = /datum/hud/construct/juggernaut base_movement_delay = 3 + ai = /datum/mob_controller/hostile/construct_armoured + +/datum/mob_controller/hostile/construct_armoured + can_escape_buckles = TRUE /obj/item/natural_weapon/juggernaut name = "armored gauntlet" @@ -227,9 +231,9 @@ environment_smash = 2 resistance = 10 construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser) - can_escape = TRUE hud_used = /datum/hud/construct/juggernaut base_movement_delay = 5 + ai = /datum/mob_controller/hostile/construct_armoured /obj/item/natural_weapon/juggernaut/behemoth force = 50 diff --git a/mods/gamemodes/cult/mobs/shade.dm b/mods/gamemodes/cult/mobs/shade.dm index 41d73a0ccca..eab5cc95131 100644 --- a/mods/gamemodes/cult/mobs/shade.dm +++ b/mods/gamemodes/cult/mobs/shade.dm @@ -6,7 +6,6 @@ max_health = 50 universal_speak = TRUE speak_emote = list("hisses") - emote_hear = list("wails","screeches") response_help_1p = "You wave your hand through $TARGET$." response_help_3p = "$USER$ waves $USER_THEIR$ hand through $TARGET$." response_disarm = "flails at" @@ -16,7 +15,6 @@ maxbodytemp = 4000 min_gas = null max_gas = null - stop_wandering = TRUE faction = "cult" supernatural = 1 status_flags = CANPUSH @@ -24,6 +22,11 @@ bleed_colour = "#181933" butchery_data = null base_movement_delay = -1 + ai = /datum/mob_controller/shade + +/datum/mob_controller/shade + emote_hear = list("wails","screeches") + do_wander = FALSE /mob/living/simple_animal/shade/check_has_mouth() return FALSE diff --git a/mods/gamemodes/cult/overrides.dm b/mods/gamemodes/cult/overrides.dm index e4cb82b5192..874dc92049c 100644 --- a/mods/gamemodes/cult/overrides.dm +++ b/mods/gamemodes/cult/overrides.dm @@ -51,13 +51,13 @@ if(ispath(MP, /mob/living/simple_animal/shade)) return 1 -/mob/living/simple_animal/hostile/faithless +/mob/living/simple_animal/hostile/revenant butchery_data = /decl/butchery_data/occult -/mob/living/simple_animal/hostile/faithless/cult +/mob/living/simple_animal/hostile/revenant/cult faction = "cult" -/mob/living/simple_animal/hostile/faithless/cult/on_defilement() +/mob/living/simple_animal/hostile/revenant/cult/on_defilement() return /obj/item/mop/Initialize() diff --git a/mods/gamemodes/cult/structures.dm b/mods/gamemodes/cult/structures.dm index 53aaa9c60ae..82b2ec1a2ac 100644 --- a/mods/gamemodes/cult/structures.dm +++ b/mods/gamemodes/cult/structures.dm @@ -88,7 +88,7 @@ spawnable=list( /mob/living/simple_animal/hostile/scarybat/cult, /mob/living/simple_animal/hostile/creature/cult, - /mob/living/simple_animal/hostile/faithless/cult + /mob/living/simple_animal/hostile/revenant/cult ) /obj/structure/door/cult diff --git a/mods/gamemodes/deity/forms/narsie/spells/tear_veil.dm b/mods/gamemodes/deity/forms/narsie/spells/tear_veil.dm index 93be7be78e6..daf6b5d59d9 100644 --- a/mods/gamemodes/deity/forms/narsie/spells/tear_veil.dm +++ b/mods/gamemodes/deity/forms/narsie/spells/tear_veil.dm @@ -14,7 +14,7 @@ var/list/possible_spawns = list( /mob/living/simple_animal/hostile/scarybat/cult, /mob/living/simple_animal/hostile/creature/cult, - /mob/living/simple_animal/hostile/faithless/cult + /mob/living/simple_animal/hostile/revenant/cult ) /spell/tear_veil/choose_targets() diff --git a/mods/gamemodes/deity/spells/open_gateway.dm b/mods/gamemodes/deity/spells/open_gateway.dm index 1038d03c6a1..db83a05c891 100644 --- a/mods/gamemodes/deity/spells/open_gateway.dm +++ b/mods/gamemodes/deity/spells/open_gateway.dm @@ -13,14 +13,14 @@ cast_sound = 'sound/effects/meteorimpact.ogg' /spell/open_gateway/choose_targets() - var/mob/living/H = holder - var/turf/T = get_turf(H) - holder.visible_message(SPAN_NOTICE("A gateway opens up underneath \the [H]!")) - var/g + var/mob/living/spellcaster = holder + var/turf/source_turf = get_turf(spellcaster) + holder.visible_message(SPAN_NOTICE("A gateway opens up underneath \the [spellcaster]!")) + var/deity var/decl/special_role/godcultist/godcult = GET_DECL(/decl/special_role/godcultist) - if(H.mind && (H.mind in godcult.current_antagonists)) - g = godcult.get_deity(H.mind) - return list(new /obj/structure/deity/gateway(T,g)) + if(spellcaster.mind && (spellcaster.mind in godcult.current_antagonists)) + deity = godcult.get_deity(spellcaster.mind) + return list(new /obj/structure/deity/gateway(source_turf, deity)) /spell/open_gateway/cast(var/list/targets, var/mob/holder, var/channel_count) if(prob((channel_count / 5) * 100)) diff --git a/mods/gamemodes/heist/heist_base.dmm b/mods/gamemodes/heist/heist_base.dmm index 9d5c5150cf7..d22f8a563e9 100644 --- a/mods/gamemodes/heist/heist_base.dmm +++ b/mods/gamemodes/heist/heist_base.dmm @@ -891,7 +891,7 @@ }, /area/map_template/syndicate_mothership/raider_base) "cj" = ( -/mob/living/simple_animal/hostile/retaliate/parrot/pirate, +/mob/living/simple_animal/hostile/parrot/pirate, /turf/unsimulated/floor{ icon_state = "asteroid" }, @@ -1011,7 +1011,7 @@ /obj/machinery/door/blast/regular/open{ id_tag = "SkipjackShuttersNorth"; name = "Blast Doors"; - + }, /obj/effect/wallframe_spawn/reinforced/titanium, /obj/effect/paint/brown, @@ -1378,7 +1378,7 @@ dir = 4; id_tag = "SkipjackShuttersWest"; name = "Skipjack Shutters"; - + }, /obj/effect/wallframe_spawn/reinforced/titanium, /obj/effect/paint/brown, @@ -1405,7 +1405,7 @@ dir = 8; id_tag = "SkipjackShuttersEast"; name = "Skipjack Shutters"; - + }, /obj/effect/wallframe_spawn/reinforced/titanium, /obj/effect/paint/brown, @@ -1477,7 +1477,7 @@ dir = 4; id_tag = "SkipjackShuttersWest"; name = "Skipjack Shutters"; - + }, /obj/effect/wallframe_spawn/reinforced/titanium, /turf/floor/plating, diff --git a/mods/gamemodes/heist/presets.dm b/mods/gamemodes/heist/presets.dm index 0d74dd38c16..17df347a08e 100644 --- a/mods/gamemodes/heist/presets.dm +++ b/mods/gamemodes/heist/presets.dm @@ -1,5 +1,8 @@ -/mob/living/simple_animal/hostile/retaliate/parrot/pirate +/mob/living/simple_animal/hostile/parrot/pirate name = "\proper Meatbag" + ai = /datum/mob_controller/aggressive/parrot/pirate + +/datum/mob_controller/aggressive/parrot/pirate emote_speech = list("Yaaar!","Squaaak!","Fight me Matey!","BAWWWWK Vox trying to eat me!") /obj/machinery/network/telecomms_hub/raider diff --git a/mods/mobs/borers/mob/borer/borer.dm b/mods/mobs/borers/mob/borer/borer.dm index 41ace823dc9..3feb93260ed 100644 --- a/mods/mobs/borers/mob/borer/borer.dm +++ b/mods/mobs/borers/mob/borer/borer.dm @@ -4,7 +4,6 @@ icon = 'mods/mobs/borers/icons/borer.dmi' desc = "A small, quivering sluglike creature." speak_emote = list("chirrups") - emote_hear = list("chirrups") response_help_3p = "$USER$ pokes $TARGET$." response_help_1p = "You poke $TARGET$." response_disarm = "prods" @@ -12,17 +11,14 @@ base_movement_delay = 5 a_intent = I_HURT - stop_wandering = TRUE status_flags = CANPUSH natural_weapon = /obj/item/natural_weapon/bite/weak - wander = 0 pass_flags = PASS_FLAG_TABLE universal_understand = TRUE holder_type = /obj/item/holder/borer mob_size = MOB_SIZE_SMALL - can_escape = TRUE - bleed_colour = "#816e12" + ai = /datum/mob_controller/borer var/static/list/chemical_types = list( "anti-trauma" = /decl/material/liquid/brute_meds, @@ -46,6 +42,11 @@ var/mob/living/human/host // Human host for the brain worm. var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. +/datum/mob_controller/borer + emote_hear = list("chirrups") + do_wander = FALSE + can_escape_buckles = TRUE + /obj/item/holder/borer origin_tech = @'{"biotech":6}' @@ -235,9 +236,7 @@ host.reset_view(null) host.machine = null - - var/mob/living/H = host - H.status_flags &= ~PASSEMOTES + host.status_flags &= ~PASSEMOTES host = null return diff --git a/mods/mobs/dionaea/datums/ai.dm b/mods/mobs/dionaea/datums/ai.dm index 5d29fc0d314..825935bfad2 100644 --- a/mods/mobs/dionaea/datums/ai.dm +++ b/mods/mobs/dionaea/datums/ai.dm @@ -1,13 +1,9 @@ /datum/mob_controller/nymph - name = "nymph" - expected_type = /mob/living/simple_animal/alien/diona - var/emote_prob = 3 - var/wander_prob = 44 - -/datum/mob_controller/nymph/do_process(var/time_elapsed) - if(body.stat != CONSCIOUS) - return - if(prob(wander_prob) && !LAZYLEN(body.grabbed_by) && isturf(body.loc)) //won't move if being pulled - body.SelfMove(pick(global.cardinal)) - if(prob(emote_prob)) - body.emote(pick(/decl/emote/visible/scratch, /decl/emote/visible/jump, /decl/emote/audible/chirp, /decl/emote/visible/tail)) + do_wander = TRUE + stop_wander_when_pulled = TRUE + emote_see = list( + /decl/emote/visible/scratch, + /decl/emote/visible/jump, + /decl/emote/audible/chirp, + /decl/emote/visible/tail + ) diff --git a/mods/species/ascent/items/id_control.dm b/mods/species/ascent/items/id_control.dm index ac465c63b3d..a3fb7a4b25f 100644 --- a/mods/species/ascent/items/id_control.dm +++ b/mods/species/ascent/items/id_control.dm @@ -51,10 +51,10 @@ if(owner) var/datum/extension/access_provider/owner_access = get_extension(owner, /datum/extension/access_provider) owner_access?.unregister_id(src) - var/mob/living/H = owner + var/mob/living/old_owner = owner . = ..() - if(H && !(locate(type) in H.get_internal_organs())) - H.remove_language(/decl/language/mantid/worldnet) + if(old_owner && !(locate(type) in old_owner.get_internal_organs())) + old_owner.remove_language(/decl/language/mantid/worldnet) /obj/item/organ/internal/controller/Initialize() if(ispath(id_card)) diff --git a/mods/species/ascent/mobs/bodyparts_insectoid.dm b/mods/species/ascent/mobs/bodyparts_insectoid.dm index 1b88459a1f1..d0053d8587e 100644 --- a/mods/species/ascent/mobs/bodyparts_insectoid.dm +++ b/mods/species/ascent/mobs/bodyparts_insectoid.dm @@ -12,19 +12,19 @@ /obj/item/organ/internal/egg_sac/insectoid/attack_self(var/mob/user) . = ..() - var/mob/living/H = user + var/mob/living/living_user = user if(.) - if(H.incapacitated()) - to_chat(H, SPAN_WARNING("You can't produce eggs in your current state.")) + if(living_user.incapacitated()) + to_chat(living_user, SPAN_WARNING("You can't produce eggs in your current state.")) return - if(H.nutrition < egg_metabolic_cost) - to_chat(H, SPAN_WARNING("You are too ravenously hungry to produce more eggs.")) + if(living_user.nutrition < egg_metabolic_cost) + to_chat(living_user, SPAN_WARNING("You are too ravenously hungry to produce more eggs.")) return - if(do_after(H, 5 SECONDS, H, FALSE)) - H.adjust_nutrition(-1 * egg_metabolic_cost) - H.visible_message(SPAN_NOTICE("\icon[H] [H] carelessly deposits an egg on \the [get_turf(src)].")) - var/obj/structure/insectoid_egg/egg = new(get_turf(H)) // splorp - egg.lineage = H.get_gyne_lineage() + if(do_after(living_user, 5 SECONDS, living_user, FALSE)) + living_user.adjust_nutrition(-1 * egg_metabolic_cost) + living_user.visible_message(SPAN_NOTICE("\icon[living_user] [living_user] carelessly deposits an egg on \the [get_turf(src)].")) + var/obj/structure/insectoid_egg/egg = new(get_turf(living_user)) // splorp + egg.lineage = living_user.get_gyne_lineage() /obj/item/organ/external/foot/insectoid/mantid name = "left tail tip" diff --git a/mods/species/vox/gear/gun.dm b/mods/species/vox/gear/gun.dm index f55e773b031..c4f62db86c9 100644 --- a/mods/species/vox/gear/gun.dm +++ b/mods/species/vox/gear/gun.dm @@ -94,7 +94,7 @@ smoke_amt = 5 smoke_spread = 1 - possible_transformations = list(/mob/living/simple_animal/hostile/retaliate/parrot/space/lesser) + possible_transformations = list(/mob/living/simple_animal/hostile/parrot/space/lesser) hud_state = "wiz_vox" diff --git a/nebula.dme b/nebula.dme index 53ed94899f9..4574dc47216 100644 --- a/nebula.dme +++ b/nebula.dme @@ -24,6 +24,7 @@ #include "code\__defines\_planes+layers.dm" #include "code\__defines\_tick.dm" #include "code\__defines\admin.dm" +#include "code\__defines\ai.dm" #include "code\__defines\ao.dm" #include "code\__defines\ao_misc.dm" #include "code\__defines\appearance.dm" @@ -327,6 +328,7 @@ #include "code\controllers\subsystems\processing\temperature.dm" #include "code\controllers\subsystems\processing\turf.dm" #include "code\controllers\subsystems\processing\vines.dm" +#include "code\datums\ai_holo.dm" #include "code\datums\ai_law_sets.dm" #include "code\datums\ai_laws.dm" #include "code\datums\beam.dm" @@ -349,10 +351,15 @@ #include "code\datums\track.dm" #include "code\datums\type_cloning.dm" #include "code\datums\weakref.dm" -#include "code\datums\ai\ai.dm" -#include "code\datums\ai\ai_holo.dm" +#include "code\datums\ai\_ai.dm" +#include "code\datums\ai\_ai_stance.dm" +#include "code\datums\ai\aggressive.dm" +#include "code\datums\ai\beast.dm" +#include "code\datums\ai\commanded.dm" #include "code\datums\ai\human.dm" +#include "code\datums\ai\hunter.dm" #include "code\datums\ai\monkey.dm" +#include "code\datums\ai\passive.dm" #include "code\datums\appearances\appearance_data.dm" #include "code\datums\appearances\appearance_manager.dm" #include "code\datums\appearances\automatic\_base.dm" @@ -540,11 +547,15 @@ #include "code\datums\music_tracks\wake.dm" #include "code\datums\observation\_defines.dm" #include "code\datums\observation\area_power_change.dm" +#include "code\datums\observation\crate_sold.dm" +#include "code\datums\observation\cyborg_created.dm" #include "code\datums\observation\death.dm" +#include "code\datums\observation\debrain.dm" #include "code\datums\observation\density_set.dm" #include "code\datums\observation\destroyed.dm" #include "code\datums\observation\dir_set.dm" #include "code\datums\observation\dismembered.dm" +#include "code\datums\observation\employee_id.dm" #include "code\datums\observation\entered.dm" #include "code\datums\observation\equipped.dm" #include "code\datums\observation\examine.dm" @@ -554,10 +565,12 @@ #include "code\datums\observation\life.dm" #include "code\datums\observation\logged_in.dm" #include "code\datums\observation\logged_out.dm" +#include "code\datums\observation\money_accounts.dm" #include "code\datums\observation\moved.dm" #include "code\datums\observation\name_set.dm" #include "code\datums\observation\observation.dm" #include "code\datums\observation\opacity_set.dm" +#include "code\datums\observation\player_latejoin.dm" #include "code\datums\observation\see_in_dark_set.dm" #include "code\datums\observation\see_invisible_set.dm" #include "code\datums\observation\set_invisibility.dm" @@ -565,6 +578,7 @@ #include "code\datums\observation\shuttle_moved.dm" #include "code\datums\observation\sight_set.dm" #include "code\datums\observation\stat_set.dm" +#include "code\datums\observation\submap_join.dm" #include "code\datums\observation\unequipped.dm" #include "code\datums\observation\updated_icon.dm" #include "code\datums\observation\zone_selected.dm" @@ -2895,10 +2909,9 @@ #include "code\modules\mob\living\silicon\robot\modules\module_security.dm" #include "code\modules\mob\living\silicon\robot\modules\module_standard.dm" #include "code\modules\mob\living\silicon\robot\modules\module_uncertified.dm" +#include "code\modules\mob\living\simple_animal\_simple_animal.dm" #include "code\modules\mob\living\simple_animal\natural_weapons.dm" -#include "code\modules\mob\living\simple_animal\simple_animal.dm" #include "code\modules\mob\living\simple_animal\simple_animal_codex.dm" -#include "code\modules\mob\living\simple_animal\simple_animal_combat.dm" #include "code\modules\mob\living\simple_animal\simple_animal_damage.dm" #include "code\modules\mob\living\simple_animal\alien\alien.dm" #include "code\modules\mob\living\simple_animal\aquatic\_aquatic.dm" @@ -2919,29 +2932,40 @@ #include "code\modules\mob\living\simple_animal\friendly\mushroom.dm" #include "code\modules\mob\living\simple_animal\friendly\possum.dm" #include "code\modules\mob\living\simple_animal\friendly\tomato.dm" +#include "code\modules\mob\living\simple_animal\hostile\_hostile.dm" #include "code\modules\mob\living\simple_animal\hostile\antlion.dm" #include "code\modules\mob\living\simple_animal\hostile\bad_drone.dm" #include "code\modules\mob\living\simple_animal\hostile\bat.dm" #include "code\modules\mob\living\simple_animal\hostile\bear.dm" #include "code\modules\mob\living\simple_animal\hostile\carp.dm" #include "code\modules\mob\living\simple_animal\hostile\creature.dm" -#include "code\modules\mob\living\simple_animal\hostile\drake.dm" #include "code\modules\mob\living\simple_animal\hostile\faithful_hound.dm" -#include "code\modules\mob\living\simple_animal\hostile\faithless.dm" -#include "code\modules\mob\living\simple_animal\hostile\giant_spider.dm" -#include "code\modules\mob\living\simple_animal\hostile\hivebot.dm" -#include "code\modules\mob\living\simple_animal\hostile\hostile.dm" #include "code\modules\mob\living\simple_animal\hostile\leech.dm" #include "code\modules\mob\living\simple_animal\hostile\mimic.dm" #include "code\modules\mob\living\simple_animal\hostile\pike.dm" +#include "code\modules\mob\living\simple_animal\hostile\revenant.dm" #include "code\modules\mob\living\simple_animal\hostile\slug.dm" +#include "code\modules\mob\living\simple_animal\hostile\space_dragon.dm" #include "code\modules\mob\living\simple_animal\hostile\tree.dm" #include "code\modules\mob\living\simple_animal\hostile\vagrant.dm" #include "code\modules\mob\living\simple_animal\hostile\viscerator.dm" -#include "code\modules\mob\living\simple_animal\hostile\commanded\_command_defines.dm" +#include "code\modules\mob\living\simple_animal\hostile\commanded\_commanded.dm" #include "code\modules\mob\living\simple_animal\hostile\commanded\bear_companion.dm" -#include "code\modules\mob\living\simple_animal\hostile\commanded\commanded.dm" #include "code\modules\mob\living\simple_animal\hostile\commanded\nanomachines.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spiders\_giant_spider.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spiders\_giant_spider_ai.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spiders\ai_guard.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spiders\ai_hunter.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spiders\ai_nurse.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spiders\guard.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spiders\hunter.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spiders\nurse.dm" +#include "code\modules\mob\living\simple_animal\hostile\giant_spiders\spitter.dm" +#include "code\modules\mob\living\simple_animal\hostile\hivebots\_hivebot.dm" +#include "code\modules\mob\living\simple_animal\hostile\hivebots\megabot.dm" +#include "code\modules\mob\living\simple_animal\hostile\hivebots\range.dm" +#include "code\modules\mob\living\simple_animal\hostile\hivebots\rapid.dm" +#include "code\modules\mob\living\simple_animal\hostile\hivebots\strong.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\exoplanet.dm" @@ -2950,7 +2974,6 @@ #include "code\modules\mob\living\simple_animal\hostile\retaliate\jelly.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\king_of_goats.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\parrot.dm" -#include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\giant_parrot\giant_parrot.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\giant_parrot\giant_parrot_species.dm" #include "code\modules\mob\living\simple_animal\passive\_passive.dm" diff --git a/test/check-paths.sh b/test/check-paths.sh index 10e06b2c36e..ea89b5ebb8c 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -24,7 +24,7 @@ exactly() { # exactly N name search [mode] [filter] # With the potential exception of << if you increase any of these numbers you're probably doing it wrong # Additional exception August 2020: \b is a regex symbol as well as a BYOND macro. exactly 1 "escapes" '\\\\(red|blue|green|black|b|i[^mc])' -exactly 5 "Del()s" '\WDel\(' +exactly 6 "Del()s" '\WDel\(' exactly 2 "/atom text paths" '"/atom' exactly 2 "/area text paths" '"/area' exactly 2 "/datum text paths" '"/datum' diff --git a/tools/map_migrations/4144_cats_faithless_drakes.txt b/tools/map_migrations/4144_cats_faithless_drakes.txt new file mode 100644 index 00000000000..dac9b507701 --- /dev/null +++ b/tools/map_migrations/4144_cats_faithless_drakes.txt @@ -0,0 +1,4 @@ +/mob/living/simple_animal/hostile/faithless/@SUBTYPES : /mob/living/simple_animal/hostile/revenant/@SUBTYPES{@OLD} +/mob/living/simple_animal/hostile/drake/@SUBTYPES : /mob/living/simple_animal/hostile/space_dragon/@SUBTYPES{@OLD} +/mob/living/simple_animal/hostile/retaliate/@SUBTYPES : /mob/living/simple_animal/hostile/@SUBTYPES{@OLD} +/mob/living/simple_animal/cat/@SUBTYPES : /mob/living/simple_animal/passive/cat/@SUBTYPES{@OLD} diff --git a/tools/map_migrations/4154_railing.txt b/tools/map_migrations/4154_railing.txt new file mode 100644 index 00000000000..bda6c039ea2 --- /dev/null +++ b/tools/map_migrations/4154_railing.txt @@ -0,0 +1 @@ +/obj/structure/railing/mapped/ebony : /obj/structure/railing/mapped/wooden/ebony{@OLD} \ No newline at end of file diff --git a/tools/map_migrations/4154_wood_doors.txt b/tools/map_migrations/4154_wood_doors.txt new file mode 100644 index 00000000000..f4c6a7622a3 --- /dev/null +++ b/tools/map_migrations/4154_wood_doors.txt @@ -0,0 +1,2 @@ +/obj/structure/door/wood/ebony : /obj/structure/door/ebony{@OLD} +/obj/structure/door/wood/walnut : /obj/structure/door/walnut{@OLD} \ No newline at end of file