diff --git a/code/__DEFINES/ai.dm b/code/__DEFINES/ai.dm
index 8533b848009..471a7925324 100644
--- a/code/__DEFINES/ai.dm
+++ b/code/__DEFINES/ai.dm
@@ -10,7 +10,7 @@
#define IS_DEAD_OR_INCAP(source) (HAS_TRAIT(source, TRAIT_INCAPACITATED) || HAS_TRAIT(source, TRAIT_HANDS_BLOCKED) || IS_IN_STASIS(source) || source.stat)
///Max pathing attempts before auto-fail
-#define MAX_PATHING_ATTEMPTS 30
+#define MAX_PATHING_ATTEMPTS 16
///Flags for ai_behavior new()
#define AI_CONTROLLER_INCOMPATIBLE (1<<0)
@@ -35,8 +35,26 @@
#define BB_MONKEY_RECRUIT_COOLDOWN "BB_monkey_recruit_cooldown"
-///Vending machine AI controller blackboard keys
+///Haunted item controller defines
+
+///Chance for haunted item to haunt someone
+#define HAUNTED_ITEM_ATTACK_HAUNT_CHANCE 10
+///Chance for haunted item to try to get itself let go.
+#define HAUNTED_ITEM_ESCAPE_GRASP_CHANCE 20
+///Chance for haunted item to warp somewhere new
+#define HAUNTED_ITEM_TELEPORT_CHANCE 4
+///Amount of aggro you get when picking up a haunted item
+#define HAUNTED_ITEM_AGGRO_ADDITION 2
+
+///Blackboard keys for haunted items
+#define BB_TO_HAUNT_LIST "BB_to_haunt_list"
+///Actual mob the item is haunting at the moment
+#define BB_HAUNT_TARGET "BB_haunt_target"
+///Amount of successful hits in a row this item has had
+#define BB_HAUNTED_THROW_ATTEMPT_COUNT "BB_haunted_throw_attempt_count"
+
+///Vending machine AI controller blackboard keys
#define BB_VENDING_CURRENT_TARGET "BB_vending_current_target"
#define BB_VENDING_TILT_COOLDOWN "BB_vending_tilt_cooldown"
#define BB_VENDING_UNTILT_COOLDOWN "BB_vending_untilt_cooldown"
diff --git a/code/__DEFINES/materials.dm b/code/__DEFINES/materials.dm
index 107e8c841ff..a2a37069e46 100644
--- a/code/__DEFINES/materials.dm
+++ b/code/__DEFINES/materials.dm
@@ -4,6 +4,9 @@
/// Hard materials, such as iron or metal
#define MAT_CATEGORY_RIGID "rigid material"
+/// Materials that can be used to craft items
+#define MAT_CATEGORY_ITEM_MATERIAL "item material"
+
///Use this flag on TRUE if you want the basic recipes
#define MAT_CATEGORY_BASE_RECIPES "basic recipes"
diff --git a/code/datums/ai/_ai_behavior.dm b/code/datums/ai/_ai_behavior.dm
index 3c5154f42a0..2961ed010ad 100644
--- a/code/datums/ai/_ai_behavior.dm
+++ b/code/datums/ai/_ai_behavior.dm
@@ -4,9 +4,12 @@
var/required_distance = 1
///Flags for extra behavior
var/behavior_flags = NONE
+ ///Cooldown between actions performances
+ var/action_cooldown = 0
///Called by the AI controller when this action is performed
/datum/ai_behavior/proc/perform(delta_time, datum/ai_controller/controller)
+ controller.behavior_cooldowns[src] = world.time + action_cooldown
return
///Called when the action is finished.
diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm
index fd794437a90..c44a00230b7 100644
--- a/code/datums/ai/_ai_controller.dm
+++ b/code/datums/ai/_ai_controller.dm
@@ -10,7 +10,9 @@ have ways of interacting with a specific atom and control it. They posses a blac
var/ai_traits
///Current actions being performed by the AI.
var/list/current_behaviors = list()
- ///Current status of AI (OFF/ON )
+ ///Current actions and their respective last time ran as an assoc list.
+ var/list/behavior_cooldowns = list()
+ ///Current status of AI (OFF/ON)
var/ai_status
///Current movement target of the AI, generally set by decision making.
var/atom/current_movement_target
@@ -91,6 +93,10 @@ have ways of interacting with a specific atom and control it. They posses a blac
var/want_to_move = FALSE
for(var/i in current_behaviors)
var/datum/ai_behavior/current_behavior = i
+
+ if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown
+ continue
+
if(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT && current_movement_target && current_behavior.required_distance < get_dist(pawn, current_movement_target)) //Move closer
want_to_move = TRUE
if(current_behavior.behavior_flags & AI_BEHAVIOR_MOVE_AND_PERFORM) //Move and perform the action
diff --git a/code/datums/ai/_item_behaviors.dm b/code/datums/ai/_item_behaviors.dm
new file mode 100644
index 00000000000..e51c7f392c5
--- /dev/null
+++ b/code/datums/ai/_item_behaviors.dm
@@ -0,0 +1,57 @@
+///This behavior is for obj/items, it is used to free themselves out of the hands of whoever is holding them
+/datum/ai_behavior/item_escape_grasp
+
+/datum/ai_behavior/item_escape_grasp/perform(delta_time, datum/ai_controller/controller)
+ . = ..()
+ var/obj/item/item_pawn = controller.pawn
+ var/mob/item_holder = item_pawn.loc
+ if(!istype(item_holder))
+ finish_action(controller, FALSE) //We're no longer beind held. abort abort!!
+ item_pawn.visible_message("[item_pawn] slips out of the hands of [item_holder]!")
+ item_holder.dropItemToGround(item_pawn, TRUE)
+ finish_action(controller, TRUE)
+
+
+///This behavior is for obj/items, it is used to move closer to a target and throw themselves towards them.
+/datum/ai_behavior/item_move_close_and_attack
+ required_distance = 3
+ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
+ action_cooldown = 20
+ ///Blackboard key for the max attempt of throws
+ var/throw_count_key
+ ///Blackboard key for the target to hit
+ var/target_key
+ ///Sound to use
+ var/attack_sound
+ ///Max attemps to make
+ var/max_attempts = 3
+
+
+/datum/ai_behavior/item_move_close_and_attack/perform(delta_time, datum/ai_controller/controller)
+ . = ..()
+ var/obj/item/item_pawn = controller.pawn
+ var/atom/throw_target = controller.blackboard[target_key]
+
+ item_pawn.visible_message("[item_pawn] hurls towards [throw_target]!")
+ item_pawn.throw_at(throw_target, rand(4,5), 9)
+ playsound(item_pawn.loc, attack_sound, 100, TRUE)
+ controller.blackboard[throw_count_key]++
+ if(controller.blackboard[throw_count_key] >= max_attempts)
+ finish_action(controller, TRUE)
+
+/datum/ai_behavior/item_move_close_and_attack/finish_action(datum/ai_controller/controller, succeeded)
+ . = ..()
+ controller.blackboard[throw_count_key] = 0
+ controller.blackboard[target_key] = null
+
+/datum/ai_behavior/item_move_close_and_attack/haunted
+ throw_count_key = BB_HAUNTED_THROW_ATTEMPT_COUNT
+ target_key = BB_HAUNT_TARGET
+ attack_sound = 'sound/items/haunted/ghostitemattack.ogg'
+ max_attempts = 4
+
+/datum/ai_behavior/item_move_close_and_attack/haunted/finish_action(datum/ai_controller/controller, succeeded)
+ var/atom/throw_target = controller.blackboard[target_key]
+ var/list/hauntee_list = controller.blackboard[BB_TO_HAUNT_LIST]
+ hauntee_list[throw_target]--
+ return ..()
diff --git a/code/datums/ai/generic_actions.dm b/code/datums/ai/generic_actions.dm
index 884f17f75f7..a3af839a568 100644
--- a/code/datums/ai/generic_actions.dm
+++ b/code/datums/ai/generic_actions.dm
@@ -10,6 +10,7 @@
var/list/screeches
/datum/ai_behavior/battle_screech/perform(delta_time, datum/ai_controller/controller)
+ . = ..()
var/mob/living/living_pawn = controller.pawn
INVOKE_ASYNC(living_pawn, /mob.proc/emote, pick(screeches))
finish_action(controller, TRUE)
diff --git a/code/datums/ai/hauntium/haunted_controller.dm b/code/datums/ai/hauntium/haunted_controller.dm
new file mode 100644
index 00000000000..9c3a1438dd6
--- /dev/null
+++ b/code/datums/ai/hauntium/haunted_controller.dm
@@ -0,0 +1,63 @@
+
+/datum/ai_controller/haunted
+ blackboard = list(BB_TO_HAUNT_LIST = list(),
+ BB_HAUNT_TARGET,
+ BB_HAUNTED_THROW_ATTEMPT_COUNT)
+
+/datum/ai_controller/haunted/TryPossessPawn(atom/new_pawn)
+ if(!isitem(new_pawn))
+ return AI_CONTROLLER_INCOMPATIBLE
+ RegisterSignal(new_pawn, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
+ return ..() //Run parent at end
+
+/datum/ai_controller/haunted/UnpossessPawn()
+ UnregisterSignal(pawn, COMSIG_ITEM_EQUIPPED)
+ return ..() //Run parent at end
+
+/datum/ai_controller/haunted/able_to_run()
+ return TRUE
+
+/datum/ai_controller/haunted/SelectBehaviors(delta_time)
+ current_behaviors = list()
+ var/obj/item/item_pawn = pawn
+
+ if(ismob(item_pawn.loc)) //We're being held, maybe escape?
+ if(DT_PROB(HAUNTED_ITEM_ESCAPE_GRASP_CHANCE, delta_time))
+ current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/item_escape_grasp)
+ return
+
+ if(!DT_PROB(HAUNTED_ITEM_ATTACK_HAUNT_CHANCE, delta_time))
+ return
+
+ var/list/to_haunt_list = blackboard[BB_TO_HAUNT_LIST]
+
+ for(var/i in to_haunt_list)
+ if(to_haunt_list[i] <= 0)
+ continue
+ var/mob/living/potential_target = i
+ if(get_dist(potential_target, item_pawn) <= 7)
+ blackboard[BB_HAUNT_TARGET] = potential_target
+ current_movement_target = potential_target
+ current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/item_move_close_and_attack/haunted)
+ return
+
+/datum/ai_controller/haunted/PerformIdleBehavior(delta_time)
+ var/obj/item/item_pawn = pawn
+ if(ismob(item_pawn.loc)) //Being held. dont teleport
+ return
+ if(DT_PROB(HAUNTED_ITEM_TELEPORT_CHANCE, delta_time))
+ playsound(item_pawn.loc, 'sound/items/haunted/ghostitemattack.ogg', 100, TRUE)
+ do_teleport(pawn, get_turf(pawn), 4, channel = TELEPORT_CHANNEL_MAGIC)
+
+///Signal response for when the item is picked up; stops listening for follow up equips, just waits for a drop.
+/datum/ai_controller/haunted/proc/on_equip(datum/source, mob/equipper, slot)
+ UnregisterSignal(pawn, COMSIG_ITEM_EQUIPPED)
+ var/list/hauntee_list = blackboard[BB_TO_HAUNT_LIST]
+ hauntee_list[equipper] = hauntee_list[equipper] + HAUNTED_ITEM_AGGRO_ADDITION //You have now become one of the victims of the HAAAAUNTTIIIINNGGG OOOOOO~~~
+ RegisterSignal(pawn, COMSIG_ITEM_DROPPED, .proc/on_dropped)
+ SIGNAL_HANDLER
+
+///Flip it so we listen for equip again but not for drop.
+/datum/ai_controller/haunted/proc/on_dropped(datum/source, mob/user)
+ RegisterSignal(pawn, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
+ UnregisterSignal(pawn, COMSIG_ITEM_DROPPED)
diff --git a/code/datums/ai/monkey/monkey_behaviors.dm b/code/datums/ai/monkey/monkey_behaviors.dm
index d052aa0f7de..16018f95940 100644
--- a/code/datums/ai/monkey/monkey_behaviors.dm
+++ b/code/datums/ai/monkey/monkey_behaviors.dm
@@ -59,12 +59,13 @@
required_distance = 0
/datum/ai_behavior/monkey_equip/ground/perform(delta_time, datum/ai_controller/controller)
+ . = ..()
equip_item(controller)
/datum/ai_behavior/monkey_equip/pickpocket
/datum/ai_behavior/monkey_equip/pickpocket/perform(delta_time, datum/ai_controller/controller)
-
+ . = ..()
if(controller.blackboard[BB_MONKEY_PICKPOCKETING]) //We are pickpocketing, don't do ANYTHING!!!!
return
INVOKE_ASYNC(src, .proc/attempt_pickpocket, controller)
diff --git a/code/datums/elements/haunted.dm b/code/datums/elements/haunted.dm
new file mode 100644
index 00000000000..5b6ef377e2c
--- /dev/null
+++ b/code/datums/elements/haunted.dm
@@ -0,0 +1,25 @@
+///Attaching this element to something will make it float, get a special ai controller, and gives it a spooky outline.
+/datum/element/haunted
+ element_flags = ELEMENT_DETACH
+
+/datum/element/haunted/Attach(datum/target)
+ . = ..()
+ if(!isitem(target))
+ return COMPONENT_INCOMPATIBLE
+ //Make em look spooky
+ var/obj/item/master = target
+ master.add_filter("haunt_glow", 2, list("type" = "outline", "color" = "#f8f8ff", "size" = 1))
+ master.ai_controller = new /datum/ai_controller/haunted(master)
+ master.AddElement(/datum/element/movetype_handler)
+ ADD_TRAIT(master, TRAIT_MOVE_FLYING, ELEMENT_TRAIT)
+
+/datum/element/haunted/Detach(datum/source, force)
+ . = ..()
+ var/atom/movable/master = source
+ master.remove_filter("haunt_glow")
+ QDEL_NULL(master.ai_controller)
+ REMOVE_TRAIT(master, TRAIT_MOVE_FLYING, ELEMENT_TRAIT)
+ master.RemoveElement(/datum/element/movetype_handler)
+ return ..()
+
+
diff --git a/code/datums/materials/alloys.dm b/code/datums/materials/alloys.dm
index eec3809e110..2bdfb857e23 100644
--- a/code/datums/materials/alloys.dm
+++ b/code/datums/materials/alloys.dm
@@ -37,7 +37,7 @@
integrity_modifier = 1.5 // Heavy duty.
armor_modifiers = list(MELEE = 1.4, BULLET = 1.4, LASER = 1.1, ENERGY = 1.1, BOMB = 1.5, BIO = 1, RAD = 1.5, FIRE = 1.1, ACID = 1)
sheet_type = /obj/item/stack/sheet/plasteel
- categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE)
+ categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
composition = list(/datum/material/iron=1, /datum/material/plasma=1)
/datum/material/alloy/plasteel/on_applied_obj(obj/item/target_item, amount, material_flags)
@@ -69,7 +69,7 @@
integrity_modifier = 1.3
armor_modifiers = list(MELEE = 1.1, BULLET = 1.1, LASER = 1.4, ENERGY = 1.4, BOMB = 1.1, BIO = 1.2, RAD = 1.1, FIRE = 1.5, ACID = 1)
sheet_type = /obj/item/stack/sheet/mineral/plastitanium
- categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE)
+ categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
composition = list(/datum/material/titanium=1, /datum/material/plasma=1)
/** Plasmaglass
@@ -87,7 +87,7 @@
sheet_type = /obj/item/stack/sheet/plasmaglass
shard_type = /obj/item/shard/plasma
value_per_unit = 0.075
- categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE)
+ categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
composition = list(/datum/material/glass=1, /datum/material/plasma=0.5)
/** Titaniumglass
@@ -104,7 +104,7 @@
sheet_type = /obj/item/stack/sheet/titaniumglass
shard_type = /obj/item/shard
value_per_unit = 0.04
- categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE)
+ categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
composition = list(/datum/material/glass=1, /datum/material/titanium=0.5)
/** Plastitanium Glass
@@ -122,7 +122,7 @@
sheet_type = /obj/item/stack/sheet/plastitaniumglass
shard_type = /obj/item/shard/plasma
value_per_unit = 0.125
- categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE)
+ categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
composition = list(/datum/material/glass=1, /datum/material/alloy/plastitanium=0.5)
/** Alien Alloy
@@ -141,7 +141,7 @@
armor_modifiers = list(MELEE = 1.4, BULLET = 1.4, LASER = 1.2, ENERGY = 1.2, BOMB = 1.5, BIO = 1.2, RAD = 1.5, FIRE = 1.2, ACID = 1.2)
sheet_type = /obj/item/stack/sheet/mineral/abductor
value_per_unit = 0.4
- categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE)
+ categories = list(MAT_CATEGORY_RIGID=TRUE, MAT_CATEGORY_BASE_RECIPES=TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
composition = list(/datum/material/iron=2, /datum/material/plasma=2)
/datum/material/alloy/alien/on_applied_obj(obj/item/target_item, amount, material_flags)
diff --git a/code/datums/materials/basemats.dm b/code/datums/materials/basemats.dm
index 7e0e6cc8baa..91e76c4f88e 100644
--- a/code/datums/materials/basemats.dm
+++ b/code/datums/materials/basemats.dm
@@ -3,7 +3,7 @@
name = "iron"
desc = "Common iron ore often found in sedimentary and igneous layers of the crust."
color = "#878687"
- categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/metal
value_per_unit = 0.0025
@@ -17,7 +17,7 @@
desc = "Glass forged by melting sand."
color = "#88cdf1"
alpha = 150
- categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
integrity_modifier = 0.1
sheet_type = /obj/item/stack/sheet/glass
shard_type = /obj/item/shard
@@ -39,7 +39,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "silver"
desc = "Silver"
color = list(255/255, 284/255, 302/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
- categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/mineral/silver
value_per_unit = 0.025
beauty_modifier = 0.075
@@ -54,7 +54,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
desc = "Gold"
color = list(340/255, 240/255, 50/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) //gold is shiny, but not as bright as bananium
strength_modifier = 1.2
- categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/mineral/gold
value_per_unit = 0.0625
beauty_modifier = 0.15
@@ -69,7 +69,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "diamond"
desc = "Highly pressurized carbon"
color = list(48/255, 272/255, 301/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
- categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/mineral/diamond
alpha = 132
value_per_unit = 0.25
@@ -85,7 +85,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "uranium"
desc = "Uranium"
color = rgb(48, 237, 26)
- categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/mineral/uranium
value_per_unit = 0.05
beauty_modifier = 0.3 //It shines so beautiful
@@ -109,7 +109,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "plasma"
desc = "Isn't plasma a state of matter? Oh whatever."
color = list(298/255, 46/255, 352/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
- categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/mineral/plasma
shard_type = /obj/item/shard/plasma
value_per_unit = 0.1
@@ -153,7 +153,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "bananium"
desc = "Material with hilarious properties"
color = list(460/255, 464/255, 0, 0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) //obnoxiously bright yellow
- categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/mineral/bananium
value_per_unit = 0.5
beauty_modifier = 0.5
@@ -195,7 +195,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
desc = "Runite"
color = "#3F9995"
strength_modifier = 1.3
- categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/mineral/runite
value_per_unit = 0.3
beauty_modifier = 0.5
@@ -309,7 +309,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
desc = "A weird kind of ice, feels warm to the touch"
color = "#88cdf1"
alpha = 150
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/hot_ice
value_per_unit = 0.2
beauty_modifier = 0.2
@@ -332,7 +332,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
desc = "Solid metallic hydrogen. Some say it should be impossible"
color = "#f2d5d7"
alpha = 150
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/mineral/metal_hydrogen
value_per_unit = 0.35
beauty_modifier = 0.35
@@ -348,7 +348,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
desc = "An unkown crystal from an unkown dimension"
color = COLOR_YELLOW
strength_modifier = 0.95
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/otherworld_crystal
value_per_unit = 0.25
beauty_modifier = 0.4
@@ -359,7 +359,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "sand"
desc = "You know, it's amazing just how structurally sound sand can be."
color = "#EDC9AF"
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/sandblock
value_per_unit = 0.001
strength_modifier = 0.5
@@ -378,7 +378,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "sandstone"
desc = "Bialtaakid 'ant taerif ma hdha."
color = "#B77D31"
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/mineral/sandstone
value_per_unit = 0.0025
armor_modifiers = list(MELEE = 0.5, BULLET = 0.5, LASER = 1.25, ENERGY = 0.5, BOMB = 0.5, BIO = 0.25, RAD = 1.5, FIRE = 1.5, ACID = 1.5)
@@ -390,7 +390,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "snow"
desc = "There's no business like snow business."
color = "#FFFFFF"
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/mineral/snow
value_per_unit = 0.0025
armor_modifiers = list(MELEE = 0.25, BULLET = 0.25, LASER = 0.25, ENERGY = 0.25, BOMB = 0.25, BIO = 0.25, RAD = 1.5, FIRE = 0.25, ACID = 1.5)
@@ -406,7 +406,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "runed metal"
desc = "Mir'ntrath barhah Nar'sie."
color = "#3C3434"
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/runed_metal
value_per_unit = 0.75
armor_modifiers = list(MELEE = 1.2, BULLET = 1.2, LASER = 1, ENERGY = 1, BOMB = 1.2, BIO = 1.2, RAD = 1.5, FIRE = 1.5, ACID = 1.5)
@@ -422,7 +422,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "bronze"
desc = "Clock Cult? Never heard of it."
color = "#92661A"
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/tile/bronze
value_per_unit = 0.025
armor_modifiers = list(MELEE = 1, BULLET = 1, LASER = 1, ENERGY = 1, BOMB = 1, BIO = 1, RAD = 1.5, FIRE = 1.5, ACID = 1.5)
@@ -432,7 +432,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "paper"
desc = "Ten thousand folds of pure starchy power."
color = "#E5DCD5"
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/paperframes
value_per_unit = 0.0025
armor_modifiers = list(MELEE = 0.1, BULLET = 0.1, LASER = 0.1, ENERGY = 0.1, BOMB = 0.1, BIO = 0.1, RAD = 1.5, FIRE = 0, ACID = 1.5)
@@ -457,7 +457,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "cardboard"
desc = "They say cardboard is used by hobos to make incredible things."
color = "#5F625C"
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/cardboard
value_per_unit = 0.003
armor_modifiers = list(MELEE = 0.25, BULLET = 0.25, LASER = 0.25, ENERGY = 0.25, BOMB = 0.25, BIO = 0.25, RAD = 1.5, FIRE = 0, ACID = 1.5)
@@ -480,7 +480,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "bone"
desc = "Man, building with this will make you the coolest caveman on the block."
color = "#e3dac9"
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/bone
value_per_unit = 0.05
armor_modifiers = list(MELEE = 1.2, BULLET = 0.75, LASER = 0.75, ENERGY = 1.2, BOMB = 1, BIO = 1, RAD = 1.5, FIRE = 1.5, ACID = 1.5)
@@ -490,7 +490,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
name = "bamboo"
desc = "If it's good enough for pandas, it's good enough for you."
color = "#339933"
- categories = list(MAT_CATEGORY_RIGID = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/mineral/bamboo
value_per_unit = 0.0025
armor_modifiers = list(MELEE = 0.5, BULLET = 0.5, LASER = 0.5, ENERGY = 0.5, BOMB = 0.5, BIO = 0.51, RAD = 1.5, FIRE = 0.5, ACID = 1.5)
diff --git a/code/datums/materials/hauntium.dm b/code/datums/materials/hauntium.dm
new file mode 100644
index 00000000000..abee58f7216
--- /dev/null
+++ b/code/datums/materials/hauntium.dm
@@ -0,0 +1,22 @@
+/datum/material/hauntium
+ name = "hauntium"
+ desc = "very scary!"
+ color = list(460/255, 464/255, 460/255, 0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
+ alpha = 100
+ categories = list(MAT_CATEGORY_ITEM_MATERIAL=TRUE)
+ sheet_type = /obj/item/stack/sheet/hauntium
+ value_per_unit = 0.05
+ beauty_modifier = 0.25
+ strength_modifier = 1
+ armor_modifiers = list(MELEE = 1, BULLET = 1, LASER = 1, ENERGY = 1, BOMB = 1, BIO = 1, RAD = 1, FIRE = 1, ACID = 1)
+
+
+/datum/material/hauntium/on_applied_obj(obj/o, amount, material_flags)
+ . = ..()
+ if(isitem(o))
+ o.AddElement(/datum/element/haunted)
+
+/datum/material/hauntium/on_removed_obj(obj/o, amount, material_flags)
+ . = ..()
+ if(isitem(o))
+ o.RemoveElement(/datum/element/haunted)
diff --git a/code/datums/materials/meat.dm b/code/datums/materials/meat.dm
index 838c57099f9..7fb29fb8493 100644
--- a/code/datums/materials/meat.dm
+++ b/code/datums/materials/meat.dm
@@ -6,7 +6,7 @@
desc = "Meat"
id = /datum/material/meat // So the bespoke versions are categorized under this
color = rgb(214, 67, 67)
- categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/meat
value_per_unit = 0.05
beauty_modifier = -0.3
diff --git a/code/datums/materials/pizza.dm b/code/datums/materials/pizza.dm
index 71967c0ec93..3a1e0cd7f78 100644
--- a/code/datums/materials/pizza.dm
+++ b/code/datums/materials/pizza.dm
@@ -4,7 +4,7 @@
name = "pizza"
desc = "~Jamme, jamme, n'coppa, jamme ja! Jamme, jamme, n'coppa jamme ja, funi-culi funi-cala funi-culi funi-cala!! Jamme jamme ja funiculi funicula!~"
color = "#FF9F23"
- categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
+ categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
sheet_type = /obj/item/stack/sheet/pizza
value_per_unit = 0.05
beauty_modifier = 0.1
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 50ef400119e..7469ca1bf19 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1852,7 +1852,7 @@
+*/
/atom/proc/InitializeAIController()
- if(ai_controller)
+ if(ispath(ai_controller))
ai_controller = new ai_controller(src)
/**
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 12fe46b9eba..63a445afae1 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -770,3 +770,22 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra
amount = 20
/obj/item/stack/sheet/sandblock/five
amount = 5
+
+
+/obj/item/stack/sheet/hauntium
+ name = "haunted sheets"
+ desc = "These sheets seem cursed."
+ singular_name = "haunted sheet"
+ icon_state = "sheet-meat"
+ material_flags = MATERIAL_COLOR
+ mats_per_unit = list(/datum/material/hauntium = MINERAL_MATERIAL_AMOUNT)
+ merge_type = /obj/item/stack/sheet/hauntium
+ material_type = /datum/material/hauntium
+ material_modifier = 1 //None of that wussy stuff
+
+/obj/item/stack/sheet/hauntium/fifty
+ amount = 50
+/obj/item/stack/sheet/hauntium/twenty
+ amount = 20
+/obj/item/stack/sheet/hauntium/five
+ amount = 5
diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm
index 9e04015935b..8ff5412eb2f 100644
--- a/code/game/objects/items/storage/toolbox.dm
+++ b/code/game/objects/items/storage/toolbox.dm
@@ -315,3 +315,11 @@
else
to_chat(user, "You need 10 floor tiles to start building a floorbot!")
return
+
+
+/obj/item/storage/toolbox/haunted
+ name = "old toolbox"
+ custom_materials = list(/datum/material/hauntium = 500)
+
+
+
diff --git a/code/modules/photography/_pictures.dm b/code/modules/photography/_pictures.dm
index 511d75ac6c3..daa0fc53c1e 100644
--- a/code/modules/photography/_pictures.dm
+++ b/code/modules/photography/_pictures.dm
@@ -11,8 +11,10 @@
var/has_blueprints = FALSE
var/logpath //If the picture has been logged this is the path.
var/id //this var is NOT protected because the worst you can do with this that you couldn't do otherwise is overwrite photos, and photos aren't going to be used as attack logs/investigations anytime soon.
+ ///Was this image capable of seeing ghosts?
+ var/see_ghosts = CAMERA_NO_GHOSTS
-/datum/picture/New(name, desc, mobs_spotted, dead_spotted, image, icon, size_x, size_y, bp, caption_, autogenerate_icon)
+/datum/picture/New(name, desc, mobs_spotted, dead_spotted, image, icon, size_x, size_y, bp, caption_, autogenerate_icon, can_see_ghosts)
if(!isnull(name))
picture_name = name
if(!isnull(desc))
@@ -35,6 +37,8 @@
caption = caption_
if(autogenerate_icon && !picture_icon && picture_image)
regenerate_small_icon()
+ if(can_see_ghosts)
+ see_ghosts = can_see_ghosts
/datum/picture/proc/get_small_icon(iconstate)
if(!picture_icon)
diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm
index 153fb049a49..6acfbcc4755 100644
--- a/code/modules/photography/camera/camera.dm
+++ b/code/modules/photography/camera/camera.dm
@@ -207,7 +207,7 @@
qdel(clone_area)
get_icon.Blend("#000", ICON_UNDERLAY)
- var/datum/picture/P = new("picture", desc.Join(" "), mobs_spotted, dead_spotted, get_icon, null, psize_x, psize_y, blueprints)
+ var/datum/picture/P = new("picture", desc.Join(" "), mobs_spotted, dead_spotted, get_icon, null, psize_x, psize_y, blueprints, see_ghosts)
after_picture(user, P, flag)
blending = FALSE
diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm
index 0bc800478f0..28120362ca5 100644
--- a/code/modules/photography/photos/photo.dm
+++ b/code/modules/photography/photos/photo.dm
@@ -10,6 +10,7 @@
resistance_flags = FLAMMABLE
max_integrity = 50
grind_results = list(/datum/reagent/iodine = 4)
+ material_flags = MATERIAL_NO_EFFECTS
var/datum/picture/picture
var/scribble //Scribble on the back.
@@ -32,6 +33,14 @@
if(setdesc && P.picture_desc)
desc = P.picture_desc
+
+ if(!P.see_ghosts) ///Dont bother with this last bit if we can't see ghosts
+ return
+ for(var/i in P.mobs_seen) //Any ghosts in the pic? its a haunted photo ooooo~
+ if(isobserver(i))
+ set_custom_materials(list(/datum/material/hauntium = 1000))
+ break
+
/obj/item/photo/update_icon_state()
if(!istype(picture) || !picture.picture_image)
return
diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm
index 00c82840431..4ebd85523c5 100644
--- a/code/modules/research/designs/autolathe_designs.dm
+++ b/code/modules/research/designs/autolathe_designs.dm
@@ -164,7 +164,7 @@
name = "Toolbox"
id = "tool_box"
build_type = AUTOLATHE
- materials = list(MAT_CATEGORY_RIGID = 500)
+ materials = list(MAT_CATEGORY_ITEM_MATERIAL = 500)
build_path = /obj/item/storage/toolbox
category = list("initial","Tools")
diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm
index ad0efcad751..e8bb1943017 100644
--- a/code/modules/research/designs/misc_designs.dm
+++ b/code/modules/research/designs/misc_designs.dm
@@ -503,7 +503,7 @@
desc = "A royal knight's favorite garments. Can be trimmed by any friendly person."
id = "knight_armour"
build_type = AUTOLATHE
- materials = list(MAT_CATEGORY_RIGID = 10000)
+ materials = list(MAT_CATEGORY_ITEM_MATERIAL = 10000)
build_path = /obj/item/clothing/suit/armor/riot/knight/greyscale
category = list("Imported")
@@ -512,7 +512,7 @@
desc = "A royal knight's favorite hat. If you hold it upside down it's actually a bucket."
id = "knight_helmet"
build_type = AUTOLATHE
- materials = list(MAT_CATEGORY_RIGID = 5000)
+ materials = list(MAT_CATEGORY_ITEM_MATERIAL = 5000)
build_path = /obj/item/clothing/head/helmet/knight/greyscale
category = list("Imported")
diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm
index 6fe3094f7b7..4ba29a42b09 100644
--- a/code/modules/research/designs/weapon_designs.dm
+++ b/code/modules/research/designs/weapon_designs.dm
@@ -325,7 +325,7 @@
desc = "A mace fit for a cleric. Useful for bypassing plate armor, but too bulky for much else."
id = "cleric_mace"
build_type = AUTOLATHE
- materials = list(MAT_CATEGORY_RIGID = 12000)
+ materials = list(MAT_CATEGORY_ITEM_MATERIAL = 12000)
build_path = /obj/item/melee/cleric_mace
category = list("Imported")
diff --git a/sound/items/haunted/ghostitemattack.ogg b/sound/items/haunted/ghostitemattack.ogg
new file mode 100644
index 00000000000..52af7d77db0
Binary files /dev/null and b/sound/items/haunted/ghostitemattack.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index e1f5b31fcc5..65ec630e4aa 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -418,7 +418,9 @@
#include "code\datums\actions\beam_rifle.dm"
#include "code\datums\ai\_ai_behavior.dm"
#include "code\datums\ai\_ai_controller.dm"
+#include "code\datums\ai\_item_behaviors.dm"
#include "code\datums\ai\generic_actions.dm"
+#include "code\datums\ai\hauntium\haunted_controller.dm"
#include "code\datums\ai\telegraph_effects.dm"
#include "code\datums\ai\monkey\monkey_behaviors.dm"
#include "code\datums\ai\monkey\monkey_controller.dm"
@@ -623,6 +625,7 @@
#include "code\datums\elements\empprotection.dm"
#include "code\datums\elements\firestacker.dm"
#include "code\datums\elements\forced_gravity.dm"
+#include "code\datums\elements\haunted.dm"
#include "code\datums\elements\item_scaling.dm"
#include "code\datums\elements\light_blocking.dm"
#include "code\datums\elements\movetype_handler.dm"
@@ -688,6 +691,7 @@
#include "code\datums\materials\_material.dm"
#include "code\datums\materials\alloys.dm"
#include "code\datums\materials\basemats.dm"
+#include "code\datums\materials\hauntium.dm"
#include "code\datums\materials\meat.dm"
#include "code\datums\materials\pizza.dm"
#include "code\datums\mood_events\_mood_event.dm"