mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-27 23:27:34 +01:00
AI actions won't unassign each other's movement targets & Mice stop being scared of people if fed cheese (#72130)
## About The Pull Request Fixes #72116 I've had a persistent issue with basic mob actions reporting this error and think I finally cracked it When replanning with `AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION` it can run `Setup` on one action leading to the plan changing, meaning that it runs `finishCommand` to cancel all other existing commands If you triggered a replan by setting up a movement action in the middle of another movement action, cancelling the existing action would remove the target already set by the current one. We want actions to be able to remove _their own_ movement target but not if it has been changed by something else in the intervening time. I fixed this by passing a source every time you set a movement target and adding a proc which only clears it if you are the source... but this feels kind of ugly. I couldn't think of anything but if you have a better idea let me know. Also while I was doing this I turned it into a feature because I'm crazy. If you feed a mouse cheese by hand it will stop being scared of humans and so will any other mice it attracts from eating more cheese. This is mostly because I think industrial mouse farming to pass cargo bounties is funny. Mice controlled by a Regal Rat lose this behaviour and forget any past loyalties they may have had. https://user-images.githubusercontent.com/7483112/208779368-3bd1da0f-4191-4405-86e5-b55a58c2cd00.mp4 Oh also I removed a block about cancelling if you have another target from the "hunt" behaviour, everywhere using this already achieves that simply by ordering the actions in expected priority order and it was messing with how I expected mice to work. Now if they happen to stop by some cheese they will correctly stop fleeing in order to eat it before continuing to run away. ## Why It's Good For The Game Fixes a bug I kept running into. Makes it possible to set up a mouse farm without them screaming constantly. Lets people more easily domesticate mice to support Ratatouille gameplay. ## Changelog 🆑 add: Mice who are fed cheese by hand will accept humans as friends, at least until reminded otherwise by their rightful lord. fix: Fixed a runtime preventing mice from acting correctly when trying to flee and also eat cheese at the same time. /🆑
This commit is contained in:
@@ -23,6 +23,16 @@
|
||||
controller.behavior_args -= type
|
||||
if(behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT) //If this was a movement task, reset our movement target if necessary
|
||||
if(!(behavior_flags & AI_BEHAVIOR_KEEP_MOVE_TARGET_ON_FINISH))
|
||||
controller.set_movement_target(null)
|
||||
clear_movement_target(controller)
|
||||
if(!(behavior_flags & AI_BEHAVIOR_KEEP_MOVING_TOWARDS_TARGET_ON_FINISH))
|
||||
controller.ai_movement.stop_moving_towards(controller)
|
||||
|
||||
/// Helper proc to ensure consistency in setting the source of the movement target
|
||||
/datum/ai_behavior/proc/set_movement_target(datum/ai_controller/controller, atom/target, datum/ai_movement/new_movement)
|
||||
controller.set_movement_target(type, target, new_movement)
|
||||
|
||||
/// Clear the controller's movement target only if it was us who last set it
|
||||
/datum/ai_behavior/proc/clear_movement_target(datum/ai_controller/controller)
|
||||
if (controller.movement_target_source != type)
|
||||
return
|
||||
controller.set_movement_target(type, null)
|
||||
|
||||
@@ -19,6 +19,8 @@ multiple modular subtrees with behaviors
|
||||
var/ai_status
|
||||
///Current movement target of the AI, generally set by decision making.
|
||||
var/atom/current_movement_target
|
||||
///Identifier for what last touched our movement target, so it can be cleared conditionally
|
||||
var/movement_target_source
|
||||
///This is a list of variables the AI uses and can be mutated by actions. When an action is performed you pass this list and any relevant keys for the variables it can mutate.
|
||||
var/list/blackboard = list()
|
||||
///Stored arguments for behaviors given during their initial creation
|
||||
@@ -64,7 +66,8 @@ multiple modular subtrees with behaviors
|
||||
return ..()
|
||||
|
||||
///Sets the current movement target, with an optional param to override the movement behavior
|
||||
/datum/ai_controller/proc/set_movement_target(atom/target, datum/ai_movement/new_movement)
|
||||
/datum/ai_controller/proc/set_movement_target(source, atom/target, datum/ai_movement/new_movement)
|
||||
movement_target_source = source
|
||||
current_movement_target = target
|
||||
if(new_movement)
|
||||
change_ai_movement_type(new_movement)
|
||||
|
||||
@@ -25,7 +25,10 @@
|
||||
/datum/ai_behavior/item_move_close_and_attack/setup(datum/ai_controller/controller, target_key, throw_count_key)
|
||||
. = ..()
|
||||
var/datum/weakref/target_ref = controller.blackboard[target_key]
|
||||
controller.set_movement_target(target_ref?.resolve())
|
||||
var/atom/target = target_ref?.resolve()
|
||||
if (isnull(target))
|
||||
return FALSE
|
||||
set_movement_target(controller, target)
|
||||
|
||||
/datum/ai_behavior/item_move_close_and_attack/perform(delta_time, datum/ai_controller/controller, target_key, throw_count_key)
|
||||
. = ..()
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
var/datum/targetting_datum/targetting_datum = controller.blackboard[targetting_datum_key]
|
||||
if (!targetting_datum)
|
||||
return
|
||||
controller.set_movement_target(target)
|
||||
set_movement_target(controller, target)
|
||||
|
||||
/datum/ai_behavior/basic_melee_attack/perform(delta_time, datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key)
|
||||
. = ..()
|
||||
@@ -52,7 +52,7 @@
|
||||
var/atom/target = weak_target?.resolve()
|
||||
if(!target)
|
||||
return FALSE
|
||||
controller.set_movement_target(target)
|
||||
set_movement_target(controller, target)
|
||||
|
||||
/datum/ai_behavior/basic_ranged_attack/perform(delta_time, datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key)
|
||||
. = ..()
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
var/atom/target = weak_target?.resolve()
|
||||
var/escaped = !target || !can_see(controller.pawn, target, run_distance) // If we can't see it we got away
|
||||
if (escaped)
|
||||
finish_action(controller, succeeded = TRUE)
|
||||
finish_action(controller, succeeded = TRUE, target_key = target_key, hiding_location_key = hiding_location_key)
|
||||
return
|
||||
if (!in_range(controller.pawn, controller.current_movement_target))
|
||||
return
|
||||
@@ -30,4 +30,8 @@
|
||||
/datum/ai_behavior/run_away_from_target/proc/plot_path_away_from(datum/ai_controller/controller, atom/target)
|
||||
var/run_direction = get_dir(controller.pawn, get_step_away(controller.pawn, target))
|
||||
var/turf/target_destination = get_ranged_target_turf(controller.pawn, run_direction, run_distance)
|
||||
controller.set_movement_target(target_destination)
|
||||
set_movement_target(controller, target_destination)
|
||||
|
||||
/datum/ai_behavior/run_away_from_target/finish_action(datum/ai_controller/controller, succeeded, target_key, hiding_location_key)
|
||||
. = ..()
|
||||
controller.blackboard[target_key] = null
|
||||
|
||||
@@ -11,5 +11,4 @@
|
||||
if(get_dist(curse_target, item_pawn) > CURSED_VIEW_RANGE)
|
||||
controller.blackboard[BB_CURSE_TARGET] = null
|
||||
return
|
||||
controller.set_movement_target(curse_target)
|
||||
controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed)
|
||||
controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed, BB_CURSE_TARGET)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
if(!controller.blackboard[BB_SIMPLE_CARRY_ITEM] && controller.blackboard[BB_FETCH_TARGET])
|
||||
var/atom/movable/interact_target = controller.blackboard[BB_FETCH_TARGET]
|
||||
if(in_range(living_pawn, interact_target) && (isturf(interact_target.loc)))
|
||||
controller.set_movement_target(interact_target)
|
||||
controller.set_movement_target(type, interact_target)
|
||||
if(IS_EDIBLE(interact_target))
|
||||
controller.queue_behavior(/datum/ai_behavior/eat_snack)
|
||||
else if(isitem(interact_target))
|
||||
@@ -35,6 +35,6 @@
|
||||
// if the return target isn't in sight, we'll just forget about it and carry the thing around
|
||||
controller.blackboard[BB_FETCH_DELIVER_TO] = null
|
||||
return
|
||||
controller.set_movement_target(return_target)
|
||||
controller.set_movement_target(type, return_target)
|
||||
controller.queue_behavior(/datum/ai_behavior/deliver_item)
|
||||
return
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
/datum/ai_behavior/break_spine/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
controller.set_movement_target(controller.blackboard[target_key])
|
||||
set_movement_target(controller, controller.blackboard[target_key])
|
||||
|
||||
/datum/ai_behavior/break_spine/perform(delta_time, datum/ai_controller/controller, target_key)
|
||||
var/mob/living/batman = controller.blackboard[target_key]
|
||||
@@ -90,7 +90,7 @@
|
||||
var/target = target_ref?.resolve()
|
||||
if(!target)
|
||||
return FALSE
|
||||
controller.set_movement_target(target)
|
||||
set_movement_target(controller, target)
|
||||
|
||||
/datum/ai_behavior/use_on_object/perform(delta_time, datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
@@ -119,7 +119,7 @@
|
||||
/datum/ai_behavior/give/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/datum/weakref/target_ref = controller.blackboard[target_key]
|
||||
controller.set_movement_target(target_ref?.resolve())
|
||||
set_movement_target(controller, target_ref?.resolve())
|
||||
|
||||
/datum/ai_behavior/give/perform(delta_time, datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
@@ -186,7 +186,7 @@
|
||||
/datum/ai_behavior/consume/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/datum/weakref/target_ref = controller.blackboard[target_key]
|
||||
controller.set_movement_target(target_ref?.resolve())
|
||||
set_movement_target(controller, target_ref?.resolve())
|
||||
|
||||
/datum/ai_behavior/consume/perform(delta_time, datum/ai_controller/controller, target_key, hunger_timer_key)
|
||||
. = ..()
|
||||
@@ -245,7 +245,7 @@
|
||||
finish_action(controller, TRUE)
|
||||
return
|
||||
|
||||
controller.set_movement_target(living_target)
|
||||
set_movement_target(controller, living_target)
|
||||
attack(controller, living_target)
|
||||
|
||||
/datum/ai_behavior/attack/finish_action(datum/ai_controller/controller, succeeded)
|
||||
@@ -281,7 +281,7 @@
|
||||
finish_action(controller, TRUE)
|
||||
return
|
||||
|
||||
controller.set_movement_target(living_target)
|
||||
set_movement_target(controller, living_target)
|
||||
|
||||
/datum/ai_behavior/follow/finish_action(datum/ai_controller/controller, succeeded)
|
||||
. = ..()
|
||||
|
||||
@@ -31,16 +31,10 @@
|
||||
if(HAS_TRAIT(controller.pawn, TRAIT_HANDS_BLOCKED) || living_pawn.stat != CONSCIOUS)
|
||||
return
|
||||
|
||||
// We're targeting something else for another reason
|
||||
var/datum/weakref/target_weakref = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
|
||||
var/atom/target = target_weakref?.resolve()
|
||||
if(!QDELETED(target))
|
||||
return
|
||||
|
||||
var/datum/weakref/hunting_weakref = controller.blackboard[target_key]
|
||||
var/atom/hunted = hunting_weakref?.resolve()
|
||||
// We're not hunting anything, look around for something
|
||||
if(QDELETED(hunted))
|
||||
if(isnull(hunted))
|
||||
controller.queue_behavior(finding_behavior, target_key, hunt_targets, hunt_range)
|
||||
|
||||
// We ARE hunting something, execute the hunt.
|
||||
@@ -85,7 +79,10 @@
|
||||
/datum/ai_behavior/hunt_target/setup(datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key)
|
||||
. = ..()
|
||||
var/datum/weakref/hunting_weakref = controller.blackboard[hunting_target_key]
|
||||
controller.set_movement_target(hunting_weakref?.resolve())
|
||||
var/atom/hunt_target = hunting_weakref?.resolve()
|
||||
if (isnull(hunt_target))
|
||||
return FALSE
|
||||
set_movement_target(controller, hunt_target)
|
||||
|
||||
/datum/ai_behavior/hunt_target/perform(delta_time, datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key)
|
||||
. = ..()
|
||||
@@ -93,7 +90,7 @@
|
||||
var/datum/weakref/hunting_weakref = controller.blackboard[hunting_target_key]
|
||||
var/atom/hunted = hunting_weakref?.resolve()
|
||||
|
||||
if(QDELETED(hunted))
|
||||
if(isnull(hunted))
|
||||
//Target is gone for some reason. forget about this task!
|
||||
controller[hunting_target_key] = null
|
||||
finish_action(controller, FALSE, hunting_target_key)
|
||||
|
||||
@@ -143,7 +143,7 @@
|
||||
/datum/ai_behavior/monkey_attack_mob/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/datum/weakref/target_ref = controller.blackboard[target_key]
|
||||
controller.set_movement_target(target_ref?.resolve())
|
||||
set_movement_target(controller, target_ref?.resolve())
|
||||
|
||||
/datum/ai_behavior/monkey_attack_mob/perform(delta_time, datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
@@ -241,7 +241,7 @@
|
||||
/datum/ai_behavior/disposal_mob/setup(datum/ai_controller/controller, attack_target_key, disposal_target_key)
|
||||
. = ..()
|
||||
var/datum/weakref/target_ref = controller.blackboard[attack_target_key]
|
||||
controller.set_movement_target(target_ref?.resolve())
|
||||
set_movement_target(controller, target_ref?.resolve())
|
||||
|
||||
/datum/ai_behavior/disposal_mob/finish_action(datum/ai_controller/controller, succeeded, attack_target_key, disposal_target_key)
|
||||
. = ..()
|
||||
@@ -259,7 +259,7 @@
|
||||
var/mob/living/target = target_ref?.resolve()
|
||||
var/mob/living/living_pawn = controller.pawn
|
||||
|
||||
controller.set_movement_target(target)
|
||||
set_movement_target(controller, target)
|
||||
|
||||
if(!target)
|
||||
finish_action(controller, FALSE)
|
||||
@@ -272,7 +272,7 @@
|
||||
|
||||
var/datum/weakref/disposal_ref = controller.blackboard[disposal_target_key]
|
||||
var/obj/machinery/disposal/disposal = disposal_ref.resolve()
|
||||
controller.set_movement_target(disposal)
|
||||
set_movement_target(controller, disposal)
|
||||
|
||||
if(!disposal)
|
||||
finish_action(controller, FALSE)
|
||||
|
||||
@@ -134,7 +134,7 @@ have ways of interacting with a specific mob and control it.
|
||||
return FALSE
|
||||
|
||||
blackboard[BB_MONKEY_PICKUPTARGET] = weapon
|
||||
set_movement_target(weapon)
|
||||
set_movement_target(type, weapon)
|
||||
if(pickpocket)
|
||||
queue_behavior(/datum/ai_behavior/monkey_equip/pickpocket)
|
||||
else
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
/datum/ai_behavior/vendor_crush/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
controller.set_movement_target(controller.blackboard[target_key])
|
||||
set_movement_target(controller, controller.blackboard[target_key])
|
||||
|
||||
|
||||
/datum/ai_behavior/vendor_crush/perform(delta_time, datum/ai_controller/controller)
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
CancelActions()
|
||||
blackboard[BB_HOSTILE_ORDER_MODE] = HOSTILE_COMMAND_FOLLOW
|
||||
blackboard[BB_FOLLOW_TARGET] = WEAKREF(commander)
|
||||
set_movement_target(commander)
|
||||
set_movement_target(type, commander)
|
||||
var/mob/living/living_pawn = pawn
|
||||
if(living_pawn.buckled)
|
||||
queue_behavior(/datum/ai_behavior/resist)//in case they are in bed or something
|
||||
@@ -211,7 +211,7 @@
|
||||
|
||||
if(blackboard[BB_HOSTILE_ORDER_MODE] == HOSTILE_COMMAND_ATTACK)
|
||||
pawn.visible_message(span_notice("[pawn] follows [pointing_friend]'s gesture towards [pointed_movable] and [blackboard[BB_HOSTILE_ATTACK_WORD]] intensely!"))
|
||||
set_movement_target(pointed_movable)
|
||||
set_movement_target(type, pointed_movable)
|
||||
blackboard[BB_ATTACK_TARGET] = WEAKREF(pointed_movable)
|
||||
if(living_pawn.buckled)
|
||||
queue_behavior(/datum/ai_behavior/resist)//in case they are in bed or something
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
/datum/ai_behavior/leave_venue/setup(datum/ai_controller/controller, venue_key)
|
||||
. = ..()
|
||||
var/datum/venue/attending_venue = controller.blackboard[venue_key]
|
||||
controller.set_movement_target(attending_venue.restaurant_portal)
|
||||
set_movement_target(controller, attending_venue.restaurant_portal)
|
||||
|
||||
/datum/ai_behavior/leave_venue/perform(delta_time, datum/ai_controller/controller, venue_key)
|
||||
. = ..()
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
controller.queue_behavior(/datum/ai_behavior/find_seat)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
controller.set_movement_target(seat_marker)
|
||||
controller.set_movement_target(type, seat_marker)
|
||||
|
||||
if(!controller.blackboard[BB_CUSTOMER_CURRENT_ORDER]) //We haven't ordered yet even ordered yet. go on! go over there and go do it!
|
||||
controller.queue_behavior(/datum/ai_behavior/order_food)
|
||||
|
||||
@@ -40,21 +40,21 @@
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
|
||||
attacker.visible_message(span_notice("[attacker] hand-feeds [food] to [parent]."), span_notice("You hand-feed [food] to [parent]."))
|
||||
qdel(food)
|
||||
if(tame)
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
if (prob(tame_chance)) //note: lack of feedback message is deliberate, keep them guessing!
|
||||
on_tame(attacker)
|
||||
on_tame(attacker, food)
|
||||
else
|
||||
tame_chance += bonus_tame_chance
|
||||
qdel(food)
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
|
||||
///Ran once taming succeeds
|
||||
/datum/component/tameable/proc/on_tame(mob/living/tamer)
|
||||
/datum/component/tameable/proc/on_tame(mob/living/tamer, atom/food)
|
||||
SIGNAL_HANDLER
|
||||
tame = TRUE
|
||||
|
||||
after_tame?.Invoke(tamer)//Run custom behavior if needed
|
||||
after_tame?.Invoke(tamer, food)//Run custom behavior if needed
|
||||
|
||||
if (isliving(parent) && isliving(tamer))
|
||||
var/mob/living/tamed = parent
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
ai_controller = /datum/ai_controller/basic_controller/mouse
|
||||
|
||||
/// Whether this rat is friendly to players
|
||||
var/tame = FALSE
|
||||
/// What color our mouse is. Brown, gray and white - leave blank for random.
|
||||
var/body_color
|
||||
/// Does this mouse contribute to the ratcap?
|
||||
@@ -36,23 +38,30 @@
|
||||
/// Probability that, if we successfully bite a shocked cable, that we will die to it.
|
||||
var/cable_zap_prob = 85
|
||||
|
||||
/mob/living/basic/mouse/Initialize(mapload)
|
||||
/mob/living/basic/mouse/Initialize(mapload, tame = FALSE)
|
||||
. = ..()
|
||||
if(contributes_to_ratcap)
|
||||
SSmobs.cheeserats |= src
|
||||
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
|
||||
|
||||
src.tame = tame
|
||||
if(isnull(body_color))
|
||||
body_color = pick("brown", "gray", "white")
|
||||
held_state = "mouse_[body_color]" // not handled by variety element
|
||||
AddElement(/datum/element/animal_variety, "mouse", body_color, FALSE)
|
||||
AddElement(/datum/element/swabable, CELL_LINE_TABLE_MOUSE, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 10)
|
||||
AddComponent(/datum/component/squeak, list('sound/effects/mousesqueek.ogg' = 1), 100, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) //as quiet as a mouse or whatever
|
||||
|
||||
var/static/list/loc_connections = list(
|
||||
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
|
||||
)
|
||||
AddElement(/datum/element/connect_loc, loc_connections)
|
||||
make_tameable()
|
||||
|
||||
/mob/living/basic/mouse/proc/make_tameable()
|
||||
if (tame)
|
||||
faction |= FACTION_NEUTRAL
|
||||
else
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cheese), tame_chance = 100, after_tame = CALLBACK(src, PROC_REF(tamed)))
|
||||
|
||||
/mob/living/basic/mouse/Destroy()
|
||||
SSmobs.cheeserats -= src
|
||||
@@ -137,6 +146,14 @@
|
||||
if(ishuman(entered) && stat == CONSCIOUS)
|
||||
to_chat(entered, span_notice("[icon2html(src, entered)] Squeak!"))
|
||||
|
||||
/// Called when a mouse is hand-fed some cheese, it will stop being afraid of humans
|
||||
/mob/living/basic/mouse/proc/tamed(mob/living/tamer, obj/item/food/cheese/cheese)
|
||||
new /obj/effect/temp_visual/heart(loc)
|
||||
faction |= FACTION_NEUTRAL
|
||||
tame = TRUE
|
||||
try_consume_cheese(cheese)
|
||||
ai_controller.CancelActions() // Interrupt any current fleeing
|
||||
|
||||
/// Attempts to consume a piece of cheese, causing a few effects.
|
||||
/mob/living/basic/mouse/proc/try_consume_cheese(obj/item/food/cheese/cheese)
|
||||
// Royal cheese will evolve us into a regal rat
|
||||
@@ -183,7 +200,7 @@
|
||||
|
||||
/// Creates a new mouse based on this mouse's subtype.
|
||||
/mob/living/basic/mouse/proc/create_a_new_rat()
|
||||
new /mob/living/basic/mouse(loc)
|
||||
new /mob/living/basic/mouse(loc, /* tame = */ tame)
|
||||
|
||||
/// Biting into a cable will cause a mouse to get shocked and die if applicable. Or do nothing if they're lucky.
|
||||
/mob/living/basic/mouse/proc/try_bite_cable(obj/structure/cable/cable)
|
||||
@@ -234,7 +251,10 @@
|
||||
response_harm_continuous = "splats"
|
||||
response_harm_simple = "splat"
|
||||
gold_core_spawnable = NO_SPAWN
|
||||
faction = list(FACTION_RAT, FACTION_MAINT_CREATURES, FACTION_NEUTRAL)
|
||||
|
||||
/mob/living/basic/mouse/brown/tom/make_tameable()
|
||||
tame = TRUE
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/mouse/brown/tom/Initialize(mapload)
|
||||
. = ..()
|
||||
@@ -243,7 +263,7 @@
|
||||
AddElement(/datum/element/pet_bonus, "squeaks happily!")
|
||||
|
||||
/mob/living/basic/mouse/brown/tom/create_a_new_rat()
|
||||
new /mob/living/basic/mouse/brown(loc) // dominant gene
|
||||
new /mob/living/basic/mouse/brown(loc, /* tame = */ tame) // dominant gene
|
||||
|
||||
/mob/living/basic/mouse/rat
|
||||
name = "rat"
|
||||
@@ -257,6 +277,9 @@
|
||||
|
||||
ai_controller = /datum/ai_controller/basic_controller/mouse/rat
|
||||
|
||||
/mob/living/basic/mouse/rat/make_tameable()
|
||||
return // Unlike in real life, space rats are horrible creatures who don't like you
|
||||
|
||||
/mob/living/basic/mouse/rat/create_a_new_rat()
|
||||
new /mob/living/basic/mouse/rat(loc)
|
||||
|
||||
@@ -349,9 +372,21 @@
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/look_for_cables,
|
||||
)
|
||||
|
||||
/// Don't look for anything to run away from if you are distracted by being adjacent to cheese
|
||||
/datum/ai_planning_subtree/flee_target/mouse
|
||||
flee_behaviour = /datum/ai_behavior/run_away_from_target/mouse
|
||||
|
||||
/datum/ai_planning_subtree/flee_target/mouse
|
||||
|
||||
/datum/ai_planning_subtree/flee_target/mouse/SelectBehaviors(datum/ai_controller/controller, delta_time)
|
||||
var/datum/weakref/hunting_weakref = controller.blackboard[BB_CURRENT_HUNTING_TARGET]
|
||||
var/atom/hunted_cheese = hunting_weakref?.resolve()
|
||||
if (!isnull(hunted_cheese))
|
||||
return // We see some cheese, which is more important than our life
|
||||
return ..()
|
||||
|
||||
/datum/ai_planning_subtree/flee_target/mouse/select
|
||||
|
||||
/datum/ai_behavior/run_away_from_target/mouse
|
||||
run_distance = 3 // Mostly exists in small tunnels, don't get ahead of yourself
|
||||
|
||||
|
||||
@@ -305,6 +305,7 @@
|
||||
/datum/action/cooldown/riot/proc/make_minion(mob/living/new_minion, minion_desc, list/command_list = mouse_commands)
|
||||
if (isbasicmob(new_minion)) // One day this will work for frogs too
|
||||
new_minion.AddComponent(/datum/component/obeys_commands, command_list)
|
||||
qdel(new_minion.GetComponent(/datum/component/tameable)) // Rats don't share
|
||||
new_minion.befriend(owner)
|
||||
new_minion.faction = owner.faction.Copy()
|
||||
// Give a hint in description too
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
return FALSE
|
||||
var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(module.mod)
|
||||
module.mod.ai_controller = mod_ai
|
||||
mod_ai.set_movement_target(imp_in)
|
||||
mod_ai.set_movement_target(type, imp_in)
|
||||
mod_ai.blackboard[BB_MOD_TARGET] = imp_in
|
||||
mod_ai.blackboard[BB_MOD_IMPLANT] = src
|
||||
module.mod.interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
|
||||
|
||||
Reference in New Issue
Block a user