From 5772ad84acb358e5b6fcb18bc2d8658a0c49af89 Mon Sep 17 00:00:00 2001 From: Neerti Date: Fri, 13 Apr 2018 11:23:45 -0400 Subject: [PATCH] Begins replacing simple_animal with simple_mob. --- code/__defines/mobs.dm | 13 + code/game/objects/effects/spiders.dm | 18 +- code/modules/ai/_defines.dm | 6 + .../ai/aI_holder_subtypes/simple_mob_ai.dm | 144 ++++++++ .../modules/ai/aI_holder_subtypes/slime_ai.dm | 102 ++++++ code/modules/ai/ai_holder.dm | 29 +- code/modules/ai/ai_holder_combat.dm | 20 +- code/modules/ai/ai_holder_communication.dm | 51 +-- code/modules/ai/ai_holder_debug.dm | 5 - code/modules/ai/ai_holder_fleeing.dm | 2 +- code/modules/ai/ai_holder_follow.dm | 10 +- code/modules/ai/ai_holder_movement.dm | 50 ++- code/modules/ai/ai_holder_targeting.dm | 60 +++- code/modules/ai/interfaces.dm | 34 +- code/modules/ai/say_list.dm | 53 ++- code/modules/mob/living/death.dm | 4 + code/modules/mob/living/living_defense.dm | 14 + code/modules/mob/living/living_defines.dm | 2 + code/modules/mob/living/login.dm | 5 + code/modules/mob/living/logout.dm | 9 +- .../mob/living/simple_animal/animals/mouse.dm | 6 +- .../mob/living/simple_mob/appearance.dm | 69 ++++ .../combat.dm} | 27 +- code/modules/mob/living/simple_mob/defense.dm | 192 +++++++++++ code/modules/mob/living/simple_mob/hands.dm | 123 +++++++ code/modules/mob/living/simple_mob/life.dm | 159 +++++++++ .../modules/mob/living/simple_mob/on_click.dm | 47 +++ .../mob/living/simple_mob/simple_hud.dm | 311 ++++++++++++++++++ .../mob/living/simple_mob/simple_mob.dm | 252 ++++++++++++++ .../simple_mob/subtypes/animal/animal.dm | 2 + .../subtypes/animal/giant_spider.dm | 64 ++++ .../subtypes/animal/giant_spider/nurse.dm | 188 +++++++++++ .../subtypes/animal/sif/hooligan_crab.dm | 59 ++++ .../simple_mob/subtypes/animal/sif/sif.dm | 3 + .../subtypes/mechanical/combat_drone.dm | 71 ++++ .../subtypes/mechanical/mechanical.dm | 20 ++ .../subtypes/mechanical/viscerator.dm | 36 ++ code/modules/mob/mob_movement.dm | 8 +- code/modules/shieldgen/directional_shield.dm | 4 + icons/mob/animal.dmi | Bin 270037 -> 270754 bytes polaris.dme | 20 +- 41 files changed, 2196 insertions(+), 96 deletions(-) create mode 100644 code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm create mode 100644 code/modules/ai/aI_holder_subtypes/slime_ai.dm create mode 100644 code/modules/mob/living/simple_mob/appearance.dm rename code/modules/mob/living/{simple_animal/simple_animal2.dm => simple_mob/combat.dm} (68%) create mode 100644 code/modules/mob/living/simple_mob/defense.dm create mode 100644 code/modules/mob/living/simple_mob/hands.dm create mode 100644 code/modules/mob/living/simple_mob/life.dm create mode 100644 code/modules/mob/living/simple_mob/on_click.dm create mode 100644 code/modules/mob/living/simple_mob/simple_hud.dm create mode 100644 code/modules/mob/living/simple_mob/simple_mob.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/animal.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/sif/sif.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index f610c0c2a3..881ba74c27 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -288,6 +288,19 @@ #define SA_ROBOTIC 3 #define SA_HUMANOID 4 +// More refined version of SA_* ""intelligence"" seperators. +// Now includes bitflags, so to target two classes you just do 'MOB_CLASS_ANIMAL|MOB_CLASS_HUMANOID' +#define MOB_CLASS_ANIMAL 1 // Simple mobs like saviks and bears. +#define MOB_CLASS_HUMANOID 2 // Non-robotic humanoids. +#define MOB_CLASS_CONSTRUCT 4 // Silicons, mechanical simple mobs, and FBPs. +#define MOB_CLASS_SLIME 8 // Everyone's favorite xenobiology specimen. +#define MOB_CLASS_ABERRATION 16 // Weird shit. +#define MOB_CLASS_DEMONIC 32 // Cult stuff. +#define MOB_CLASS_BOSS 64 // Future megafauna hopefully someday. +#define MOB_CLASS_ILLUSION 128 // Fake mobs, e.g. Technomancer illusions. + +#define MOB_CLASS_ALL (MOB_CLASS_ANIMAL|MOB_CLASS_HUMANOID|MOB_CLASS_CONSTRUCT|MOB_CLASS_SLIME|MOB_CLASS_ABERRATION|MOB_CLASS_DEMONIC|MOB_CLASS_BOSS|MOB_CLASS_ILLUSION) + // For slime commanding. Higher numbers allow for more actions. #define SLIME_COMMAND_OBEY 1 // When disciplined. #define SLIME_COMMAND_FACTION 2 // When in the same 'faction'. diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index d3b9853e06..5f0a1e7f45 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -56,9 +56,11 @@ /obj/effect/spider/stickyweb icon_state = "stickyweb1" - New() - if(prob(50)) - icon_state = "stickyweb2" + +/obj/effect/spider/stickyweb/initialize() + if(prob(50)) + icon_state = "stickyweb2" + return ..() /obj/effect/spider/stickyweb/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) if(air_group || (height==0)) return 1 @@ -80,10 +82,12 @@ var/spiders_min = 6 var/spiders_max = 24 var/spider_type = /obj/effect/spider/spiderling - New() - pixel_x = rand(3,-3) - pixel_y = rand(3,-3) - processing_objects |= src + +/obj/effect/spider/eggcluster/initialize() + pixel_x = rand(3,-3) + pixel_y = rand(3,-3) + processing_objects |= src + return ..() /obj/effect/spider/eggcluster/New(var/location, var/atom/parent) get_light_and_color(parent) diff --git a/code/modules/ai/_defines.dm b/code/modules/ai/_defines.dm index 354f428e88..c72158134e 100644 --- a/code/modules/ai/_defines.dm +++ b/code/modules/ai/_defines.dm @@ -14,6 +14,12 @@ #define AI_LOG_DEBUG 4 // More detailed information about the flow of execution. #define AI_LOG_TRACE 5 // Even more detailed than the last. +// Results of pre-movement checks. +// Todo: Move outside AI code? +#define MOVEMENT_ON_COOLDOWN -1 // Recently moved and needs to try again soon. +#define MOVEMENT_FAILED 0 // Move() returned false for whatever reason and the mob didn't move. +#define MOVEMENT_SUCCESSFUL 1 // Move() returned true and the mob hopefully moved. + // Reasons for targets to not be valid. Based on why, the AI responds differently. #define AI_TARGET_VALID 0 // We can fight them. #define AI_TARGET_INVIS 1 // They were in field of view but became invisible. Switch to STANCE_BLINDFIGHT if no other viable targets exist. diff --git a/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm b/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm new file mode 100644 index 0000000000..206ce2c705 --- /dev/null +++ b/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm @@ -0,0 +1,144 @@ +// AIs for simple mobs. + +/datum/ai_holder/simple_mob + hostile = TRUE // The majority of simplemobs are hostile. + cooperative = TRUE + returns_home = TRUE + can_flee = FALSE + speak_chance = 1 // If the mob's saylist is empty, nothing will happen. + +// For animals. +/datum/ai_holder/simple_mob/passive + hostile = FALSE + wander = TRUE + can_flee = TRUE + +// Ranged mobs. + +/datum/ai_holder/simple_mob/ranged + ranged = TRUE + +// Tries to not waste ammo. +/datum/ai_holder/simple_mob/ranged/careful + conserve_ammo = TRUE + +// Runs away from its target if within a certain distance. +/datum/ai_holder/simple_mob/ranged/kiting + pointblank = TRUE // So we don't need to copypaste post_melee_attack(). + var/run_if_this_close = 4 // If anything gets within this range, it'll try to move away. + +/datum/ai_holder/simple_mob/ranged/kiting/threatening + threaten = TRUE + threaten_delay = 1 SECOND // Less of a threat and more of pre-attack notice. + threaten_timeout = 30 SECONDS + +/datum/ai_holder/simple_mob/ranged/kiting/post_ranged_attack(atom/A) + if(get_dist(holder, A) < run_if_this_close) + holder.IMove(get_step_away(holder, A, run_if_this_close)) + holder.face_atom(A) + + +// Melee mobs. + +/datum/ai_holder/simple_mob/melee + +// Dances around the enemy its fighting, making it harder to fight back. +/datum/ai_holder/simple_mob/melee/evasive + +/datum/ai_holder/simple_mob/melee/evasive/post_melee_attack(atom/A) + if(holder.Adjacent(A)) + holder.IMove(get_step(holder, pick(alldirs))) + holder.face_atom(A) + +// The AI for hooligan crabs. Follows people for awhile. +/datum/ai_holder/simple_mob/melee/hooligan + hostile = FALSE + retaliate = TRUE + max_home_distance = 12 + var/random_follow = TRUE // Turn off if you want to bus with crabs. + +/datum/ai_holder/simple_mob/melee/hooligan/handle_stance_strategical() + ..() + if(random_follow && stance == STANCE_IDLE && !leader) + if(prob(10)) + for(var/mob/living/L in hearers(holder)) + if(!istype(L, holder)) // Don't follow other hooligan crabs. + holder.visible_message("\The [holder] starts to follow \the [L].") + set_follow(L, rand(20 SECONDS, 40 SECONDS)) + + +// The AI for nurse spiders. Wraps things in webs by 'attacking' them. +/datum/ai_holder/simple_mob/melee/nurse_spider + wander = TRUE + base_wander_delay = 8 + +// Get us unachored objects as an option as well. +/datum/ai_holder/simple_mob/melee/nurse_spider/list_targets() + . = ..() + + var/static/alternative_targets = typecacheof(list(/obj/item, /obj/structure)) + + for(var/AT in typecache_filter_list(range(vision_range, holder), alternative_targets)) + var/obj/O = AT + if(can_see(holder, O, vision_range) && !O.anchored) + . += O + +// Select an obj if no mobs are around. +/datum/ai_holder/melee/nurse_spider/pick_target(list/targets) + var/mobs_only = locate(/mob/living) in targets // If a mob is in the list of targets, then ignore objects. + if(mobs_only) + for(var/A in targets) + if(!isliving(A)) + targets -= A + + return ..(targets) + +/datum/ai_holder/simple_mob/melee/nurse_spider/can_attack(atom/movable/the_target) + . = ..() + if(!.) // Parent returned FALSE. + if(istype(the_target, /obj)) + var/obj/O = the_target + if(!O.anchored) + return TRUE + +/* + + + +/datum/ai_holder/simple_mob/melee/nurse_spider/list_targets() + var/list/targets = ..() + + if(targets.len) // Do regular targeting if there's actual enemies. + world << "Returned early." + world << "targets was [english_list(targets)]." + return targets + + // Otherwise lets target objects to web them. + var/static/webbable_types = typecacheof(list(/obj/machinery, /obj/item/, /obj/structure)) + for(var/WT in typecache_filter_list(range(vision_range, holder), webbable_types)) + var/obj/O = WT + if(!O.anchored && can_see(holder, O, vision_range)) + targets += WT + + world << "targets was [english_list(targets)]." + return targets +*/ +/* +/datum/ai_holder/simple_mob/melee/nurse_spider/can_attack(atom/movable/the_target) + . = ..() + if(!.) // Parent returned FALSE. + if(istype(the_target, /obj)) + var/obj/O = the_target + if(!O.anchored) + return TRUE +*/ + +/* + . = hearers(vision_range, holder) - src // Remove ourselves to prevent suicidal decisions. + + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha)) + + for(var/HM in typecache_filter_list(range(vision_range, holder), hostile_machines)) + if(can_see(holder, HM, vision_range)) + . += HM +*/ \ No newline at end of file diff --git a/code/modules/ai/aI_holder_subtypes/slime_ai.dm b/code/modules/ai/aI_holder_subtypes/slime_ai.dm new file mode 100644 index 0000000000..905bb4b109 --- /dev/null +++ b/code/modules/ai/aI_holder_subtypes/slime_ai.dm @@ -0,0 +1,102 @@ +// Specialized AI for slime simplemobs. +// Unlike the parent AI code, this will probably break a lot of things if you put it on something that isn't /mob/living/simple_animal/slime + +/datum/ai_holder/normal/slime + +/datum/ai_holder/normal/slime/New() + ..() + ASSERT(istype(holder, /mob/living/simple_animal/slime)) + +/datum/ai_holder/normal/slime/find_target(var/list/possible_targets, var/has_targets_list = FALSE) + var/mob/living/simple_animal/slime/me = holder + if(me.victim) // Don't worry about finding another target if we're eatting someone. + return + if(leader && me.can_command(leader)) // If following someone, don't attack until the leader says so, something hits you, or the leader is no longer worthy. + return + ..() + +/datum/ai_holder/normal/slime/found(mob/living/L) + var/mob/living/simple_animal/slime/me = holder + if(isliving(L)) + if(can_attack(L)) + if(L.faction == me.faction && !attack_same_faction) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(istype(H.species, /datum/species/monkey)) // istype() is so they'll eat the alien monkeys too. + return H // Monkeys are always food. + else + return + + // if(L in friends) + // return + + if(istype(L, /mob/living/simple_animal/slime)) + var/mob/living/simple_animal/slime/buddy = L + if(buddy.slime_color == me.slime_color || me.discipline || me.unity || buddy.unity) + return // Don't hurt same colored slimes. + else + return buddy //do hurt others + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(istype(H.species, /datum/species/monkey)) // istype() is so they'll eat the alien monkeys too. + return H // Monkeys are always food. + + if(issilicon(L) || isbot(L)) + if(me.discipline && !me.rabid) + return // We're a good slime. For now at least. + return + return + +/datum/ai_holder/normal/slime/closest_distance() + if(isliving(target)) + var/mob/living/L = target + if(L.stat) + return 1 // Melee (eat) the target if dying, don't shoot it. + return ..() + + +/datum/ai_holder/normal/slime/help_requested(var/mob/living/simple_animal/slime/buddy) + var/mob/living/simple_animal/slime/me = holder + if(istype(buddy)) + if(buddy.slime_color != me.slime_color && (!me.unity || !buddy.unity)) // We only help slimes of the same color, if it's another slime calling for help. + // ai_log("HelpRequested() by [buddy] but they are a [buddy.slime_color] while we are a [src.slime_color].",2) + ai_log("help_requested() : Help was requested by [buddy] but they are a [buddy.slime_color] while we are a [me.slime_color].", AI_LOG_INFO) + return + ..() + + +/datum/ai_holder/normal/slime/handle_resist() + var/mob/living/simple_animal/slime/me = holder + if(me.buckled && me.victim && isliving(me.buckled) && me.victim == me.buckled) // If it's buckled to a living thing it's probably eating it. + return + else + ..() + + +/datum/ai_holder/normal/slime/melee_attack(atom/A) + var/mob/living/simple_animal/slime/me = holder + if(isliving(A)) + var/mob/living/L = A + if( (!L.lying && prob(60 + (me.power_charge * 4) ) || (!L.lying && me.optimal_combat) )) // "Smart" slimes always stun first. + me.a_intent = I_DISARM // Stun them first. + else if(me.can_consume(L) && L.lying) + me.a_intent = I_GRAB // Then eat them. + else + me.a_intent = I_HURT // Otherwise robust them. + ..(A) + +/* +/mob/living/simple_animal/slime/PunchTarget() + if(victim) + return // Already eatting someone. + if(!client) // AI controlled. + if( (!target_mob.lying && prob(60 + (power_charge * 4) ) || (!target_mob.lying && optimal_combat) )) // "Smart" slimes always stun first. + a_intent = I_DISARM // Stun them first. + else if(can_consume(target_mob) && target_mob.lying) + a_intent = I_GRAB // Then eat them. + else + a_intent = I_HURT // Otherwise robust them. + ai_log("PunchTarget() will [a_intent] [target_mob]",2) + ..() +*/ \ No newline at end of file diff --git a/code/modules/ai/ai_holder.dm b/code/modules/ai/ai_holder.dm index 41c5210833..f41627fd4f 100644 --- a/code/modules/ai/ai_holder.dm +++ b/code/modules/ai/ai_holder.dm @@ -19,6 +19,10 @@ var/mob/living/holder = null // The mob this datum is going to control. var/stance = STANCE_IDLE // Determines if the mob should be doing a specific thing, e.g. attacking, following, standing around, etc. var/intelligence_level = AI_NORMAL // Adjust to make the AI be intentionally dumber, or make it more robust (e.g. dodging grenades). + var/autopilot = FALSE // If true, the AI won't be deactivated if a client gets attached to the AI's mob. + var/busy = FALSE // If true, the ticker will skip processing this mob until this is false. Good for if you need the + // mob to stay still (e.g. delayed attacking). If you need the mob to be inactive for an extended period of time, + // consider sleeping the AI instead. @@ -73,11 +77,14 @@ // 'Tactical' processes such as moving a step, meleeing an enemy, firing a projectile, and other fairly cheap actions that need to happen quickly. /datum/ai_holder/proc/handle_tactics() + if(busy) + return handle_stance_tactical() // 'Strategical' processes that are more expensive on the CPU and so don't get run as often as the above proc, such as A* pathfinding or robust targeting. /datum/ai_holder/proc/handle_strategicals() -// world << "[holder.name] Strategicals!" + if(busy) + return handle_stance_strategical() /* @@ -301,3 +308,23 @@ return FALSE return TRUE + +// Helper proc to turn AI 'busy' mode on or off without having to check if there is an AI, to simplify writing code. +/mob/living/proc/set_AI_busy(value) + if(ai_holder) + ai_holder.busy = value + +/mob/living/proc/is_AI_busy() + if(!ai_holder) + return FALSE + return ai_holder.busy + +// Helper proc to check for the AI's stance. +// Returns null if there's no AI holder, or the mob has a player and autopilot is not on. +// Otherwise returns the stance. +/mob/living/proc/get_AI_stance() + if(!ai_holder) + return null + if(client && !ai_holder.autopilot) + return null + return ai_holder.stance \ No newline at end of file diff --git a/code/modules/ai/ai_holder_combat.dm b/code/modules/ai/ai_holder_combat.dm index d749761d1e..1f904c1f24 100644 --- a/code/modules/ai/ai_holder_combat.dm +++ b/code/modules/ai/ai_holder_combat.dm @@ -86,12 +86,16 @@ set_stance(STANCE_APPROACH) // We're not entirely sure how holder will do melee attacks since any /mob/living could be holder, but we don't have to care because Interfaces. -/datum/ai_holder/proc/melee_attack(atom/movable/AM) - return holder.IAttack(AM) +/datum/ai_holder/proc/melee_attack(atom/A) + . = holder.IAttack(A) + if(.) + post_melee_attack(A) // Ditto. -/datum/ai_holder/proc/ranged_attack(atom/movable/AM) - return holder.IRangedAttack(AM) +/datum/ai_holder/proc/ranged_attack(atom/A) + . = holder.IRangedAttack(A) + if(.) + post_ranged_attack(A) // Most mobs probably won't have this defined but we don't care. /datum/ai_holder/proc/special_attack(atom/movable/AM) @@ -100,7 +104,13 @@ // Called when within striking/shooting distance, however cooldown is not considered. // Override to do things like move in a random step for evasiveness. // Note that this is called BEFORE the attack. -/datum/ai_holder/proc/on_engagement(atom/movable/AM) +/datum/ai_holder/proc/on_engagement(atom/A) + +// Called after a successful (IE not on cooldown) ranged attack. +/datum/ai_holder/proc/post_ranged_attack(atom/A) + +// Ditto but for melee. +/datum/ai_holder/proc/post_melee_attack(atom/A) // Used to make sure projectiles will probably hit the target and not the wall or a friend. /datum/ai_holder/proc/test_projectile_safety(atom/movable/AM) diff --git a/code/modules/ai/ai_holder_communication.dm b/code/modules/ai/ai_holder_communication.dm index ba49fa29e6..9ed3d53728 100644 --- a/code/modules/ai/ai_holder_communication.dm +++ b/code/modules/ai/ai_holder_communication.dm @@ -7,17 +7,9 @@ var/threaten_timeout = 1 MINUTE // If the mob threatens someone, they leave, and then come back before this timeout period, the mob escalates to fighting immediately. var/last_conflict_time = null // Last occurance of threatening or fighting being used, in world.time. - var/threaten_sound = null // Sound file played when the mob calls threaten_target() for the first time. - var/stand_down_sound = null // Sound file played when the mob loses sight of the threatened target. - var/speak_chance = 0 // Probability that the mob talks (this is 'X in 200' chance since even 1/100 is pretty noisy) var/reacts = 0 // Reacts to some things being said. - var/datum/say_list/say_list = null // Datum containing all of our lines. - var/say_list_type = /datum/say_list // Type to give us on initialization. Default has empty lists, so the mob will be silent. -/datum/ai_holder/New() - ..() - say_list = new say_list_type() /datum/ai_holder/proc/should_threaten() if(!threaten) @@ -37,25 +29,28 @@ threatening = TRUE last_conflict_time = world.time - holder.say(safepick(say_list.say_threaten)) - playsound(holder.loc, threaten_sound, 75, 1) // We do this twice to make the sound -very- noticable to the target. - playsound(target.loc, threaten_sound, 75, 1) // Actual aim-mode also does that so at least it's consistant. + if(holder.say_list) + holder.say(safepick(holder.say_list.say_threaten)) + playsound(holder.loc, holder.say_list.threaten_sound, 50, 1) // We do this twice to make the sound -very- noticable to the target. + playsound(target.loc, holder.say_list.threaten_sound, 50, 1) // Actual aim-mode also does that so at least it's consistant. else // Otherwise we are waiting for them to go away or to wait long enough for escalate. if(target in list_targets()) // Are they still visible? if(threaten_delay && last_conflict_time + threaten_delay < world.time) // Waited too long. threatening = FALSE set_stance(STANCE_APPROACH) - holder.say(safepick(say_list.say_escalate)) + if(holder.say_list) + holder.say(safepick(holder.say_list.say_escalate)) else return // Wait a bit. else // They left, or so we think. threatening = FALSE set_stance(STANCE_IDLE) - holder.say(safepick(say_list.say_stand_down)) - playsound(holder.loc, stand_down_sound, 50, 1) // We do this twice to make the sound -very- noticable to the target. - playsound(target.loc, stand_down_sound, 50, 1) // Actual aim-mode also does that so at least it's consistant. + if(holder.say_list) + holder.say(safepick(holder.say_list.say_stand_down)) + playsound(holder.loc, holder.say_list.stand_down_sound, 50, 1) // We do this twice to make the sound -very- noticable to the target. + playsound(target.loc, holder.say_list.stand_down_sound, 50, 1) // Actual aim-mode also does that so at least it's consistant. // Determines what is deserving of a warning when STANCE_ALERT is active. /datum/ai_holder/proc/will_threaten(mob/living/the_target) @@ -75,13 +70,25 @@ /datum/ai_holder/proc/handle_idle_speaking() if(rand(0,200) < speak_chance) - var/list/comm_types = list() // What kinds of things can we do? + // Check if anyone is around to 'appreciate' what we say. + var/alone = TRUE + for(var/m in viewers(holder)) + var/mob/M = m + if(M.client) + alone = FALSE + break + if(alone) // Forever alone. No point doing anything else. + return - if(say_list.speak.len) + var/list/comm_types = list() // What kinds of things can we do? + if(!holder.say_list) + return + + if(holder.say_list.speak.len) comm_types += COMM_SAY - if(say_list.emote_hear.len) + if(holder.say_list.emote_hear.len) comm_types += COMM_AUDIBLE_EMOTE - if(say_list.emote_see.len) + if(holder.say_list.emote_see.len) comm_types += COMM_VISUAL_EMOTE if(!comm_types.len) @@ -89,11 +96,11 @@ switch(pick(comm_types)) if(COMM_SAY) - holder.say(safepick(say_list.speak)) + holder.say(safepick(holder.say_list.speak)) if(COMM_AUDIBLE_EMOTE) - holder.audible_emote(safepick(say_list.emote_hear)) + holder.audible_emote(safepick(holder.say_list.emote_hear)) if(COMM_VISUAL_EMOTE) - holder.visible_emote(safepick(say_list.emote_see)) + holder.visible_emote(safepick(holder.say_list.emote_see)) #undef COMM_SAY #undef COMM_AUDIBLE_EMOTE diff --git a/code/modules/ai/ai_holder_debug.dm b/code/modules/ai/ai_holder_debug.dm index 538d74e895..c4b2f990a0 100644 --- a/code/modules/ai/ai_holder_debug.dm +++ b/code/modules/ai/ai_holder_debug.dm @@ -102,9 +102,6 @@ threaten = TRUE wander = TRUE - threaten_sound = 'sound/weapons/TargetOn.ogg' - stand_down_sound = 'sound/weapons/TargetOff.ogg' - /datum/ai_holder/hostile/ranged/debug wander = FALSE conserve_ammo = FALSE @@ -115,8 +112,6 @@ last_turf_display = TRUE debug_ai = AI_LOG_INFO - say_list_type = /datum/say_list/pirate - /mob/living/simple_animal/hostile/pirate hostile = FALSE ai_inactive = TRUE diff --git a/code/modules/ai/ai_holder_fleeing.dm b/code/modules/ai/ai_holder_fleeing.dm index bde630ef57..b9c45ace93 100644 --- a/code/modules/ai/ai_holder_fleeing.dm +++ b/code/modules/ai/ai_holder_fleeing.dm @@ -16,7 +16,7 @@ return TRUE if(can_flee) - if(!hostile) + if(!hostile && !retaliate) return TRUE // We're not hostile and someone attacked us first. if(flee_when_dying && (holder.health / holder.getMaxHealth()) <= dying_threshold) return TRUE // We're gonna die! diff --git a/code/modules/ai/ai_holder_follow.dm b/code/modules/ai/ai_holder_follow.dm index a9f1433c6b..9329fe53d7 100644 --- a/code/modules/ai/ai_holder_follow.dm +++ b/code/modules/ai/ai_holder_follow.dm @@ -57,4 +57,12 @@ ai_log("lose_follow() : Exited.", AI_LOG_DEBUG) /datum/ai_holder/proc/should_follow_leader() - return leader && get_dist(holder, leader) > follow_distance \ No newline at end of file + if(!leader) + return FALSE + if(follow_until_time && world.time > follow_until_time) + lose_follow() + set_stance(STANCE_IDLE) + return FALSE + if(get_dist(holder, leader) > follow_distance) + return TRUE + return FALSE \ No newline at end of file diff --git a/code/modules/ai/ai_holder_movement.dm b/code/modules/ai/ai_holder_movement.dm index 3a15fa5dc1..55b2e2ce34 100644 --- a/code/modules/ai/ai_holder_movement.dm +++ b/code/modules/ai/ai_holder_movement.dm @@ -6,7 +6,9 @@ // Home. var/turf/home_turf = null // The mob's 'home' turf. It will try to stay near it if told to do so. var/returns_home = FALSE // If true, makes the mob go to its 'home' if it strays too far. + var/home_low_priority = FALSE // If true, the mob will not go home unless it has nothing better to do, e.g. its following someone. var/max_home_distance = 3 // How far the mob can go away from its home before being told to go_home(). + // Note that there is a 'BYOND cap' of 14 due to limitations of get_/step_to(). // Wandering. var/wander = FALSE // If true, the mob will randomly move in the four cardinal directions when idle. @@ -15,7 +17,6 @@ var/wander_when_pulled = FALSE // If the mob will refrain from wandering if someone is pulling it. - /datum/ai_holder/proc/walk_to_destination() ai_log("walk_to_destination() : Entering.",AI_LOG_DEBUG) if(!destination) @@ -41,7 +42,15 @@ ai_log("walk_to_destination() : Exiting.",AI_LOG_DEBUG) /datum/ai_holder/proc/should_go_home() - return (returns_home && home_turf) && (get_dist(holder, home_turf) > max_home_distance) + if(!returns_home || !home_turf) + return FALSE + if(get_dist(holder, home_turf) > max_home_distance) + if(!home_low_priority) + return TRUE + else if(!leader && !target) + return TRUE + return FALSE +// return (returns_home && home_turf) && (get_dist(holder, home_turf) > max_home_distance) /datum/ai_holder/proc/go_home() if(home_turf) @@ -70,7 +79,6 @@ // Walk towards whatever. /datum/ai_holder/proc/walk_path(atom/A, get_to = 1) ai_log("walk_path() : Entered.", AI_LOG_DEBUG) - var/turf/pre_step_turf = get_turf(holder) if(use_astar) if(!path.len) // If we're missing a path, make a new one. @@ -78,13 +86,14 @@ calculate_path(A, get_to) if(!path.len) // If we still don't have one, then the target's probably somewhere inaccessible to us. Get as close as we can. - ai_log("walk_path() : Failed to obtain path to target. Using step_to() instead.", AI_LOG_INFO) - step_to(holder, A) - if(get_turf(holder) == pre_step_turf) + ai_log("walk_path() : Failed to obtain path to target. Using get_step_to() instead.", AI_LOG_INFO) + // step_to(holder, A) + if(holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED) + ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_TRACE) breakthrough(A) // We failed to move, time to smash things. return - if(!move_once()) // Start walking the path. + if(move_once() == FALSE) // Start walking the path. ai_log("walk_path() : Failed to step.", AI_LOG_TRACE) ++failed_steps if(failed_steps > 3) // We're probably stuck. @@ -93,8 +102,10 @@ failed_steps = 0 else - step_to(holder, A) - if(get_turf(holder) == pre_step_turf) + // step_to(holder, A) + ai_log("walk_path() : Going to IMove().", AI_LOG_TRACE) + if(holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED ) + ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_TRACE) breakthrough(A) // We failed to move, time to smash things. ai_log("walk_path() : Exited.", AI_LOG_DEBUG) @@ -110,14 +121,17 @@ var/turf/T = src.path[1] T.overlays -= path_overlay - step_towards(holder, src.path[1]) - if(holder.loc != src.path[1]) - ai_log("move_once() : Failed step. Exiting.", AI_LOG_TRACE) - return FALSE - else - path -= src.path[1] - ai_log("move_once() : Successful step. Exiting.", AI_LOG_TRACE) - return TRUE +// step_towards(holder, src.path[1]) + if(holder.IMove(get_step_towards(holder, src.path[1])) != MOVEMENT_ON_COOLDOWN) + if(holder.loc != src.path[1]) + ai_log("move_once() : Failed step. Exiting.", AI_LOG_TRACE) + return MOVEMENT_FAILED + else + path -= src.path[1] + ai_log("move_once() : Successful step. Exiting.", AI_LOG_TRACE) + return MOVEMENT_SUCCESSFUL + ai_log("move_once() : Mob movement on cooldown. Exiting.", AI_LOG_TRACE) + return MOVEMENT_ON_COOLDOWN /datum/ai_holder/proc/should_wander() return wander && !leader @@ -135,6 +149,6 @@ var/moving_to = 0 // Apparently this is required or it always picks 4, according to the previous developer for simplemob AI. moving_to = pick(cardinal) holder.set_dir(moving_to) - holder.Move(get_step(holder,moving_to)) + holder.IMove(get_step(holder,moving_to)) wander_delay = base_wander_delay ai_log("handle_wander_movement() : Exited.", AI_LOG_DEBUG) diff --git a/code/modules/ai/ai_holder_targeting.dm b/code/modules/ai/ai_holder_targeting.dm index 5de7b2890c..3eaa33f449 100644 --- a/code/modules/ai/ai_holder_targeting.dm +++ b/code/modules/ai/ai_holder_targeting.dm @@ -1,8 +1,8 @@ // Used for assigning a target for attacking. /datum/ai_holder - var/hostile = FALSE // Do we try to hurt others? Setting to false disables most combat processing. - var/retaliate = FALSE // Attacks whatever struck it first. + var/hostile = FALSE // Do we try to hurt others? + var/retaliate = FALSE // Attacks whatever struck it first. Mobs will still attack back if this is false but hostile is true. var/atom/movable/target = null // The thing (mob or object) we're trying to kill. var/turf/target_last_seen_turf = null // Where the mob last observed the target being, used if the target disappears but the mob wants to keep fighting. @@ -26,6 +26,8 @@ // Step 2, filter down possible targets to things we actually care about. /datum/ai_holder/proc/find_target(var/list/possible_targets, var/has_targets_list = FALSE) + if(!hostile) // So retaliating mobs only attack the thing that hit it. + return null . = list() if(!has_targets_list) possible_targets = list_targets() @@ -45,12 +47,13 @@ // Step 3, pick among the possible, attackable targets. /datum/ai_holder/proc/pick_target(list/targets) if(target != null) // If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets. - for(var/possible_target in targets) - var/atom/A = possible_target - var/target_dist = get_dist(holder, target) - var/possible_target_distance = get_dist(holder, A) - if(target_dist < possible_target_distance) - targets -= A + targets = target_filter_distance(targets) +// for(var/possible_target in targets) +// var/atom/A = possible_target +// var/target_dist = get_dist(holder, target) +// var/possible_target_distance = get_dist(holder, A) +// if(target_dist < possible_target_distance) +// targets -= A if(!targets.len) // We found nothing. return var/chosen_target = pick(targets) @@ -66,6 +69,18 @@ set_stance(STANCE_APPROACH) return TRUE +// Filters return one or more 'preferred' targets. + +// This one is for closest targets. +/datum/ai_holder/proc/target_filter_distance(list/targets) + for(var/possible_target in targets) + var/atom/A = possible_target + var/target_dist = get_dist(holder, target) + var/possible_target_distance = get_dist(holder, A) + if(target_dist < possible_target_distance) + targets -= A + return targets + /datum/ai_holder/proc/can_attack(atom/movable/the_target) if(!can_see_target(the_target)) return FALSE @@ -156,4 +171,31 @@ if(last_turf_display && target_last_seen_turf) target_last_seen_turf.overlays -= last_turf_overlay ai_log("lose_target_position() : Last position is being reset.", AI_LOG_INFO) - target_last_seen_turf = null \ No newline at end of file + target_last_seen_turf = null + +// Responds to a hostile action against its mob. +/datum/ai_holder/proc/react_to_attack(atom/movable/attacker) + if(holder.stat) // We're dead. + ai_log("react_to_attack() : Was attacked by [attacker], but we are dead/unconscious.", AI_LOG_TRACE) + return FALSE + if(!hostile && !retaliate) // Not allowed to defend ourselves. + ai_log("react_to_attack() : Was attacked by [attacker], but we are not allowed to attack back.", AI_LOG_TRACE) + return FALSE + if(holder.IIsAlly(attacker)) // I'll overlook it THIS time... + ai_log("react_to_attack() : Was attacked by [attacker], but they were an ally.", AI_LOG_TRACE) + return FALSE + if(target) // Already fighting someone. Switching every time we get hit would impact our combat performance. + ai_log("react_to_attack() : Was attacked by [attacker], but we already have a target.", AI_LOG_TRACE) + return FALSE + + if(stance == STANCE_SLEEP) // If we're asleep, try waking up if someone's wailing on us. + ai_log("react_to_attack() : AI is asleep. Waking up.", AI_LOG_TRACE) + go_wake() + + ai_log("react_to_attack() : Was attacked by [attacker].", AI_LOG_INFO) + return give_target(attacker) // Also handles setting the appropiate stance. + +/* + if(ai_inactive || stat || M == target_mob) return //Not if we're dead or already hitting them + if(M in friends || M.faction == faction) return //I'll overlook it THIS time... +*/ \ No newline at end of file diff --git a/code/modules/ai/interfaces.dm b/code/modules/ai/interfaces.dm index b38b1ce7a3..9ef507ceeb 100644 --- a/code/modules/ai/interfaces.dm +++ b/code/modules/ai/interfaces.dm @@ -6,20 +6,20 @@ /mob/living/proc/IAttack(atom/A) return FALSE -/mob/living/simple_animal/IAttack(atom/A) +/mob/living/simple_mob/IAttack(atom/A) return attack_target(A) /mob/living/proc/IRangedAttack(atom/A) return FALSE -/mob/living/simple_animal/IRangedAttack(atom/A) +/mob/living/simple_mob/IRangedAttack(atom/A) return shoot_target(A) /mob/living/proc/ISpecialAttack(atom/A) return FALSE -/mob/living/simple_animal/ISpecialAttack(atom/A) - return special_attack_target() +/mob/living/simple_mob/ISpecialAttack(atom/A) + return special_attack_target(A) /mob/living/proc/ISay(message) @@ -31,8 +31,32 @@ if(!.) // Outside the faction, try to see if they're friends. return L in friends +/mob/living/simple_mob/IIsAlly(mob/living/L) + . = ..() + if(!.) // Outside the faction, try to see if they're friends. + return L in friends + /mob/living/proc/IGetID() /mob/living/simple_animal/IGetID() if(myid) - return myid.GetID() \ No newline at end of file + return myid.GetID() + +// Respects move cooldowns as if it had a client. +/mob/living/proc/IMove(newloc) + if(check_move_cooldown()) +// if(!newdir) +// newdir = get_dir(get_turf(src), newloc) + + // Move()ing to another tile successfully returns 32 because BYOND. Would rather deal with TRUE/FALSE-esque terms. + // Note that moving to the same tile will be 'successful'. + var/turf/old_T = get_turf(src) + . = SelfMove(newloc) ? MOVEMENT_SUCCESSFUL : MOVEMENT_FAILED + if(. == MOVEMENT_SUCCESSFUL) + set_dir(get_dir(old_T, newloc)) + // Apply movement delay. + // Player movement has more factors but its all in the client and fixing that would be its own project. + setMoveCooldown(movement_delay()) + return + + . = MOVEMENT_ON_COOLDOWN // To avoid superfast mobs that aren't meant to be superfast. Is actually -1. diff --git a/code/modules/ai/say_list.dm b/code/modules/ai/say_list.dm index 29cd476eee..37f14823e5 100644 --- a/code/modules/ai/say_list.dm +++ b/code/modules/ai/say_list.dm @@ -1,13 +1,29 @@ -// A simple datum that just holds many lists of lines for AI mobs to pick from. +// A simple datum that just holds many lists of lines for mobs to pick from. // This is its own datum in order to be able to have different types of mobs be able to use the same lines if desired, // even when inheritence wouldn't be able to do so. // Also note this also contains emotes, despite its name. +// and now sounds because its probably better that way. + +/mob/living + var/datum/say_list/say_list = null + var/say_list_type = /datum/say_list // Type to give us on initialization. Default has empty lists, so the mob will be silent. + +/mob/living/initialize() + if(say_list_type) + say_list = new say_list_type(src) + return ..() + +/mob/living/Destroy() + qdel_null(say_list) + return ..() + /datum/say_list var/list/speak = list() // Things the mob might say if it talks while idle. var/list/emote_hear = list() // Hearable emotes it might perform var/list/emote_see = list() // 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/say_understood = list() // When accepting an order. var/list/say_cannot = list() // When they cannot comply. var/list/say_maybe_target = list() // When they briefly see something. @@ -16,6 +32,11 @@ var/list/say_stand_down = list() // When the threatened thing goes away. var/list/say_escalate = list() // When the threatened thing doesn't go away. + var/threaten_sound = null // Sound file played when the mob's AI calls threaten_target() for the first time. + var/stand_down_sound = null // Sound file played when the mob's AI loses sight of the threatened target. + + + @@ -25,10 +46,38 @@ // This one's pretty dumb, but pirates are dumb anyways and it makes for a good test. /datum/say_list/pirate speak = list("Yarr!") + say_understood = list("Alright, matey.") say_cannot = list("No, matey.") say_maybe_target = list("Eh?") say_got_target = list("Yarrrr!") say_threaten = list("You best leave, this booty is mine.", "No plank to walk on, just walk away.") say_stand_down = list("Good.") - say_escalate = list("Yarr! The booty is mine!") \ No newline at end of file + say_escalate = list("Yarr! The booty is mine!") + +/datum/say_list/malf_drone + speak = 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") + + say_understood = list("Affirmative.", "Positive.") + say_cannot = list("Denied.", "Negative.") + say_maybe_target = list("Possible threat detected. Investigating.", "Motion detected.", "Investigating.") + say_got_target = list("Threat detected.", "New task: Remove threat.", "Threat removal engaged.", "Engaging target.") + say_threaten = list("Motion detected, judging target...") + say_stand_down = list("Visual lost.", "Error: Target not found.") + say_escalate = list("Viable target found. Removing.", "Engaging target.", "Target judgement complete. Removal required.") + + threaten_sound = 'sound/effects/turret/move1.wav' + stand_down_sound = 'sound/effects/turret/move2.wav' + +/datum/say_list/mercenary + threaten_sound = 'sound/weapons/TargetOn.ogg' + stand_down_sound = 'sound/weapons/TargetOff.ogg' + + +/datum/say_list/crab + emote_hear = list("clicks") + emote_see = list("clacks") + +/datum/say_list/spider + emote_hear = list("chitters") \ No newline at end of file diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index d6c2bc0405..6812323a4e 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -1,3 +1,7 @@ /mob/living/death() clear_fullscreens() + + if(ai_holder) + ai_holder.go_sleep() + . = ..() \ No newline at end of file diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 3d9140dd46..e0e85edbcd 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -92,6 +92,13 @@ /mob/living/proc/getsoak(var/def_zone, var/type) return 0 +// Clicking with an empty hand +/mob/living/attack_hand(mob/living/L) + ..() + if(istype(L) && L.a_intent != I_HELP) + if(ai_holder) // Using disarm, grab, or harm intent is considered a hostile action to the mob's AI. + ai_holder.react_to_attack(L) + /mob/living/bullet_act(var/obj/item/projectile/P, var/def_zone) //Being hit while using a deadman switch @@ -102,6 +109,9 @@ src.visible_message("[src] triggers their deadman's switch!") signaler.signal() + if(ai_holder && P.firer) + ai_holder.react_to_attack(P.firer) + //Armor var/soaked = get_armor_soak(def_zone, P.check_armour, P.armor_penetration) var/absorb = run_armor_check(def_zone, P.check_armour, P.armor_penetration) @@ -272,6 +282,8 @@ var/client/assailant = M.client if(assailant) add_attack_logs(M,src,"Hit by thrown [O.name]") + if(ai_holder) + ai_holder.react_to_attack(O.thrower) // Begin BS12 momentum-transfer code. var/mass = 1.5 @@ -335,6 +347,8 @@ adjustBruteLoss(damage) add_attack_logs(user,src,"Generic attack (probably animal)", admin_notify = FALSE) //Usually due to simple_animal attacks + if(ai_holder) + ai_holder.react_to_attack(user) src.visible_message("[user] has [attack_message] [src]!") user.do_attack_animation(src) spawn(1) updatehealth() diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index e9faaa0769..30c5580d14 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -5,6 +5,8 @@ var/maxHealth = 100 //Maximum health that should be possible. Avoid adjusting this if you can, and instead use modifiers datums. var/health = 100 //A mob's health + var/mob_class = MOB_CLASS_ANIMAL // A mob's "class", e.g. human, mechanical, animal, etc. Used for certain projectile effects. See __defines/mob.dm for available classes. + var/hud_updateflag = 0 //Damage related vars, NOTE: THESE SHOULD ONLY BE MODIFIED BY PROCS diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm index 275e88be08..96d59dc330 100644 --- a/code/modules/mob/living/login.dm +++ b/code/modules/mob/living/login.dm @@ -8,4 +8,9 @@ update_antag_icons(mind) client.screen |= global_hud.darksight client.images |= dsoverlay + + if(ai_holder && !ai_holder.autopilot) + ai_holder.go_sleep() + to_chat(src,"Mob AI disabled while you are controlling the mob.") + return . diff --git a/code/modules/mob/living/logout.dm b/code/modules/mob/living/logout.dm index 577221a03e..fa04b0b317 100644 --- a/code/modules/mob/living/logout.dm +++ b/code/modules/mob/living/logout.dm @@ -1,6 +1,11 @@ /mob/living/Logout() ..() - if (mind) + if (mind) //Per BYOND docs key remains set if the player DCs, becomes null if switching bodies. - if(!key) //key and mind have become seperated. + if(!key) //key and mind have become seperated. mind.active = 0 //This is to stop say, a mind.transfer_to call on a corpse causing a ghost to re-enter its body. + + spawn(15 SECONDS) //15 seconds to get back into the mob before it goes wild + if(src && !src.client) + if(ai_holder) + ai_holder.go_wake() diff --git a/code/modules/mob/living/simple_animal/animals/mouse.dm b/code/modules/mob/living/simple_animal/animals/mouse.dm index 123e82a611..42d6ef6423 100644 --- a/code/modules/mob/living/simple_animal/animals/mouse.dm +++ b/code/modules/mob/living/simple_animal/animals/mouse.dm @@ -16,10 +16,10 @@ see_in_dark = 6 universal_understand = 1 - mob_size = MOB_MINISCULE + mob_size = MOB_SMALL pass_flags = PASSTABLE - can_pull_size = ITEMSIZE_TINY - can_pull_mobs = MOB_PULL_NONE +// can_pull_size = ITEMSIZE_TINY +// can_pull_mobs = MOB_PULL_NONE layer = MOB_LAYER density = 0 diff --git a/code/modules/mob/living/simple_mob/appearance.dm b/code/modules/mob/living/simple_mob/appearance.dm new file mode 100644 index 0000000000..2c8bc5efd8 --- /dev/null +++ b/code/modules/mob/living/simple_mob/appearance.dm @@ -0,0 +1,69 @@ +/mob/living/simple_mob/update_icon() + . = ..() + var/mutable_appearance/ma = new(src) + ma.layer = layer + ma.plane = plane + + ma.overlays = list(modifier_overlay) + + //Awake and normal + if((stat == CONSCIOUS) && (!icon_rest || !resting || !incapacitated(INCAPACITATION_DISABLED) )) + ma.icon_state = icon_living + + //Dead + else if(stat >= DEAD) + ma.icon_state = icon_dead + + //Resting or KO'd + else if(((stat == UNCONSCIOUS) || resting || incapacitated(INCAPACITATION_DISABLED) ) && icon_rest) + ma.icon_state = icon_rest + + //Backup + else + ma.icon_state = initial(icon_state) + + if(has_hands) + if(r_hand_sprite) + ma.overlays += r_hand_sprite + if(l_hand_sprite) + ma.overlays += l_hand_sprite + + if(has_eye_glow) + add_eyes() + + appearance = ma + + +// If your simple mob's update_icon() call calls overlays.Cut(), this needs to be called after this, or manually apply modifier_overly to overlays. +/mob/living/simple_mob/update_modifier_visuals() + var/image/effects = null + if(modifier_overlay) + overlays -= modifier_overlay + modifier_overlay.overlays.Cut() + effects = modifier_overlay + else + effects = new() + + for(var/datum/modifier/M in modifiers) + if(M.mob_overlay_state) + var/image/I = image("icon" = 'icons/mob/modifier_effects.dmi', "icon_state" = M.mob_overlay_state) + I.appearance_flags = RESET_COLOR // So colored mobs don't affect the overlay. + effects.overlays += I + + modifier_overlay = effects + overlays += modifier_overlay + + +/mob/living/simple_mob/proc/add_eyes() + if(!eye_layer) + eye_layer = image(icon, "[icon_state]-eyes") + eye_layer.plane = PLANE_LIGHTING_ABOVE + + overlays += eye_layer + +/mob/living/simple_mob/proc/remove_eyes() + overlays -= eye_layer + + +/mob/living/simple_mob/gib() + ..(icon_gib,1,icon) // we need to specify where the gib animation is stored \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/simple_animal2.dm b/code/modules/mob/living/simple_mob/combat.dm similarity index 68% rename from code/modules/mob/living/simple_animal/simple_animal2.dm rename to code/modules/mob/living/simple_mob/combat.dm index 04522eb101..d7f784c2be 100644 --- a/code/modules/mob/living/simple_animal/simple_animal2.dm +++ b/code/modules/mob/living/simple_mob/combat.dm @@ -1,8 +1,5 @@ -// Reorganized and somewhat cleaned up. -// AI code has been made into a datum, inside the AI module folder. - // Does a melee attack. -/mob/living/simple_animal/proc/attack_target(atom/A) +/mob/living/simple_mob/proc/attack_target(atom/A) set waitfor = FALSE // For attack animations, if they're ever added. Don't want the AI processor to get held up. if(!A.Adjacent(src)) @@ -13,8 +10,10 @@ return do_attack(A) + // This does the actual attack. -/mob/living/simple_animal/proc/do_attack(atom/A) +// This is a seperate proc for the purposes of attack animations. +/mob/living/simple_mob/proc/do_attack(atom/A) if(!A.Adjacent(src)) // They could've moved in the meantime. return FALSE @@ -27,10 +26,8 @@ if(isliving(A)) // Check defenses. var/mob/living/L = A - if(prob(melee_miss_chance)) // This is stupid (The logging part). - src.attack_log += text("\[[time_stamp()]\] attacked [L.name] ([L.ckey])") - L.attack_log += text("\[[time_stamp()]\] was attacked by [src.name] ([src.ckey])") - visible_message("[src] misses [L]!") + if(prob(melee_miss_chance)) + add_attack_logs(src, L, "Animal-attacked (miss)", admin_notify = FALSE) do_attack_animation(src) return FALSE // We missed. @@ -40,12 +37,16 @@ return FALSE // We were blocked. if(A.attack_generic(src, damage_to_do, pick(attacktext)) && attack_sound) + apply_melee_effects(A) playsound(src, attack_sound, 75, 1) return TRUE +// Override for special effects after a successful attack. +/mob/living/simple_mob/proc/apply_melee_effects(atom/A) + //The actual top-level ranged attack proc -/mob/living/simple_animal/proc/shoot_target(atom/A) +/mob/living/simple_mob/proc/shoot_target(atom/A) if(!canClick()) return FALSE @@ -58,8 +59,9 @@ return TRUE + //Shoot a bullet at someone (idk why user is an argument when src would fit???) -/mob/living/simple_animal/proc/shoot(atom/A, turf/start, mob/living/user, bullet = 0) +/mob/living/simple_mob/proc/shoot(atom/A, turf/start, mob/living/user, bullet = 0) if(A == start) return @@ -69,6 +71,7 @@ return P.launch(A) + //Special attacks, like grenades or blinding spit or whatever -/mob/living/simple_animal/proc/special_attack_target(atom/A) +/mob/living/simple_mob/proc/special_attack_target(atom/A) return FALSE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/defense.dm b/code/modules/mob/living/simple_mob/defense.dm new file mode 100644 index 0000000000..dac6bfeafc --- /dev/null +++ b/code/modules/mob/living/simple_mob/defense.dm @@ -0,0 +1,192 @@ +// Hit by a projectile. +/mob/living/simple_mob/bullet_act(var/obj/item/projectile/P) + //Projectiles with bonus SA damage + if(!P.nodamage) + // if(!P.SA_vulnerability || P.SA_vulnerability == intelligence_level) + if(P.SA_vulnerability & mob_class) + P.damage += P.SA_bonus_damage + + . = ..() + + +// When someone clicks us with an empty hand +/mob/living/simple_mob/attack_hand(mob/living/L) + ..() + + switch(L.a_intent) + if(I_HELP) + if(health > 0) + L.visible_message("\The [L] [response_help] \the [src].") + + if(I_DISARM) + L.visible_message("\The [L] [response_disarm] \the [src].") + L.do_attack_animation(src) + //TODO: Push the mob away or something + + if(I_GRAB) + if (L == src) + return + if (!(status_flags & CANPUSH)) + return + if(!incapacitated(INCAPACITATION_ALL) && prob(grab_resist)) + L.visible_message("\The [L] tries to grab \the [src] but fails!") + return + + var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(L, src) + + L.put_in_active_hand(G) + + G.synch() + G.affecting = src + LAssailant = L + + L.visible_message("\The [L] has grabbed [src] passively!") + L.do_attack_animation(src) + + if(I_HURT) + var/armor = run_armor_check(def_zone = null, attack_flag = "melee") + apply_damage(damage = harm_intent_damage, damagetype = BURN, def_zone = null, blocked = armor, blocked = resistance, used_weapon = null, sharp = FALSE, edge = FALSE) + L.visible_message("\The [L] [response_harm] \the [src]!") + L.do_attack_animation(src) + + return + + +// When somoene clicks us with an item in hand +/mob/living/simple_mob/attackby(var/obj/item/O, var/mob/user) + if(istype(O, /obj/item/stack/medical)) + if(stat != DEAD) + // This could be done better. + var/obj/item/stack/medical/MED = O + if(health < getMaxHealth()) + if(MED.amount >= 1) + adjustBruteLoss(-MED.heal_brute) + MED.amount -= 1 + if(MED.amount <= 0) + qdel(MED) + visible_message("\The [user] applies the [MED] on [src].") + else + var/datum/gender/T = gender_datums[src.get_visible_gender()] + to_chat(user, "\The [src] is dead, medical items won't bring [T.him] back to life.") // the gender lookup is somewhat overkill, but it functions identically to the obsolete gender macros and future-proofs this code + if(meat_type && (stat == DEAD)) //if the animal has a meat, and if it is dead. + if(istype(O, /obj/item/weapon/material/knife) || istype(O, /obj/item/weapon/material/knife/butch)) + harvest(user) + + return ..() + + +// Handles the actual harming by a melee weapon. +/mob/living/simple_mob/hit_with_weapon(obj/item/O, mob/living/user, var/effective_force, var/hit_zone) + effective_force = O.force + + //Animals can't be stunned(?) + if(O.damtype == HALLOSS) + effective_force = 0 + if(supernatural && istype(O,/obj/item/weapon/nullrod)) + effective_force *= 2 + purge = 3 + if(O.force <= resistance) + to_chat(user,"This weapon is ineffective, it does no damage.") + return 2 //??? + +// react_to_attack(user) + + . = ..() + + +// Exploding. +/mob/living/simple_mob/ex_act(severity) + if(!blinded) + flash_eyes() + var/armor = run_armor_check(def_zone = null, attack_flag = "bomb") + var/bombdam = 500 + switch (severity) + if (1.0) + bombdam = 500 + if (2.0) + bombdam = 60 + if (3.0) + bombdam = 30 + + apply_damage(damage = bombdam, damagetype = BRUTE, def_zone = null, blocked = armor, blocked = resistance, used_weapon = null, sharp = FALSE, edge = FALSE) + + if(bombdam > maxHealth) + gib() + + +// Fire stuff. Not really exciting at the moment. +/mob/living/simple_mob/handle_fire() + return +/mob/living/simple_mob/update_fire() + return +/mob/living/simple_mob/IgniteMob() + return +/mob/living/simple_mob/ExtinguishMob() + return + + +// Electricity +/mob/living/simple_mob/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null) + shock_damage *= siemens_coeff + if(shock_damage < 1) + return 0 + + apply_damage(damage = shock_damage, damagetype = BURN, def_zone = null, blocked = null, blocked = resistance, used_weapon = null, sharp = FALSE, edge = FALSE) + playsound(loc, "sparks", 50, 1, -1) + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, loc) + s.start() + + +// Shot with taser/stunvolver +/mob/living/simple_mob/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null) + if(taser_kill) + var/stunDam = 0 + var/agonyDam = 0 + var/armor = run_armor_check(def_zone = null, attack_flag = "energy") + + if(stun_amount) + stunDam += stun_amount * 0.5 + apply_damage(damage = stunDam, damagetype = BURN, def_zone = null, blocked = armor, blocked = resistance, used_weapon = used_weapon, sharp = FALSE, edge = FALSE) + + if(agony_amount) + agonyDam += agony_amount * 0.5 + apply_damage(damage = agonyDam, damagetype = BURN, def_zone = null, blocked = armor, blocked = resistance, used_weapon = used_weapon, sharp = FALSE, edge = FALSE) + + +// Electromagnetism +/mob/living/simple_mob/emp_act(severity) + ..() // To emp_act() its contents. + if(!isSynthetic()) + return + switch(severity) + if(1) + // adjustFireLoss(rand(15, 25)) + adjustFireLoss(min(60, getMaxHealth()*0.5)) // Weak mobs will always take two direct EMP hits to kill. Stronger ones might take more. + if(2) + adjustFireLoss(min(30, getMaxHealth()*0.25)) + // adjustFireLoss(rand(10, 18)) + if(3) + adjustFireLoss(min(15, getMaxHealth()*0.125)) + // adjustFireLoss(rand(5, 12)) + if(4) + adjustFireLoss(min(7, getMaxHealth()*0.0625)) + // adjustFireLoss(rand(1, 6)) + + +// Armor +/mob/living/simple_mob/getarmor(def_zone, attack_flag) + var/armorval = armor[attack_flag] + if(!armorval) + return 0 + else + return armorval + +/mob/living/simple_mob/getsoak(def_zone, attack_flag) + var/armorval = armor_soak[attack_flag] + if(!armorval) + return 0 + else + return armorval + diff --git a/code/modules/mob/living/simple_mob/hands.dm b/code/modules/mob/living/simple_mob/hands.dm new file mode 100644 index 0000000000..52da3b75d7 --- /dev/null +++ b/code/modules/mob/living/simple_mob/hands.dm @@ -0,0 +1,123 @@ +// Hand procs for player-controlled SA's +/mob/living/simple_mob/swap_hand() + src.hand = !( src.hand ) + if(hud_used.l_hand_hud_object && hud_used.r_hand_hud_object) + if(hand) //This being 1 means the left hand is in use + hud_used.l_hand_hud_object.icon_state = "l_hand_active" + hud_used.r_hand_hud_object.icon_state = "r_hand_inactive" + else + hud_used.l_hand_hud_object.icon_state = "l_hand_inactive" + hud_used.r_hand_hud_object.icon_state = "r_hand_active" + return + +/mob/living/simple_mob/put_in_active_hand(var/obj/item/I) + if(!has_hands || !istype(I)) + return + +//Puts the item into our active hand if possible. returns 1 on success. +/mob/living/simple_mob/put_in_active_hand(var/obj/item/W) + if(!has_hands) + return FALSE + return (hand ? put_in_l_hand(W) : put_in_r_hand(W)) + +/mob/living/simple_mob/put_in_l_hand(var/obj/item/W) + if(!..() || l_hand) + return 0 + W.forceMove(src) + l_hand = W + W.equipped(src,slot_l_hand) + W.add_fingerprint(src) + update_inv_l_hand() + return TRUE + +/mob/living/simple_mob/put_in_r_hand(var/obj/item/W) + if(!..() || r_hand) + return 0 + W.forceMove(src) + r_hand = W + W.equipped(src,slot_r_hand) + W.add_fingerprint(src) + update_inv_r_hand() + return TRUE + +/mob/living/simple_mob/update_inv_r_hand() + if(QDESTROYING(src)) + return + + if(r_hand) + r_hand.screen_loc = ui_rhand //TODO + + //determine icon state to use + var/t_state + if(r_hand.item_state_slots && r_hand.item_state_slots[slot_r_hand_str]) + t_state = r_hand.item_state_slots[slot_r_hand_str] + else if(r_hand.item_state) + t_state = r_hand.item_state + else + t_state = r_hand.icon_state + + //determine icon to use + var/icon/t_icon + if(r_hand.item_icons && (slot_r_hand_str in r_hand.item_icons)) + t_icon = r_hand.item_icons[slot_r_hand_str] + else if(r_hand.icon_override) + t_state += "_r" + t_icon = r_hand.icon_override + else + t_icon = INV_R_HAND_DEF_ICON + + //apply color + var/image/standing = image(icon = t_icon, icon_state = t_state) + standing.color = r_hand.color + + r_hand_sprite = standing + + else + r_hand_sprite = null + + update_icon() + +/mob/living/simple_mob/update_inv_l_hand() + if(QDESTROYING(src)) + return + + if(l_hand) + l_hand.screen_loc = ui_lhand //TODO + + //determine icon state to use + var/t_state + if(l_hand.item_state_slots && l_hand.item_state_slots[slot_l_hand_str]) + t_state = l_hand.item_state_slots[slot_l_hand_str] + else if(l_hand.item_state) + t_state = l_hand.item_state + else + t_state = l_hand.icon_state + + //determine icon to use + var/icon/t_icon + if(l_hand.item_icons && (slot_l_hand_str in l_hand.item_icons)) + t_icon = l_hand.item_icons[slot_l_hand_str] + else if(l_hand.icon_override) + t_state += "_l" + t_icon = l_hand.icon_override + else + t_icon = INV_L_HAND_DEF_ICON + + //apply color + var/image/standing = image(icon = t_icon, icon_state = t_state) + standing.color = l_hand.color + + l_hand_sprite = standing + + else + l_hand_sprite = null + + update_icon() + +//Can insert extra huds into the hud holder here. +/mob/living/simple_mob/proc/extra_huds(var/datum/hud/hud,var/icon/ui_style,var/list/hud_elements) + return + +//If they can or cannot use tools/machines/etc +/mob/living/simple_mob/IsAdvancedToolUser() + return has_hands \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/life.dm b/code/modules/mob/living/simple_mob/life.dm new file mode 100644 index 0000000000..2b3fbf50ed --- /dev/null +++ b/code/modules/mob/living/simple_mob/life.dm @@ -0,0 +1,159 @@ +/mob/living/simple_mob/Life() + ..() + + //Health + updatehealth() + if(stat >= DEAD) + return FALSE + + handle_stunned() + handle_weakened() + handle_paralysed() + handle_supernatural() + handle_atmos() + + handle_special() + + return TRUE + + +//Should we be dead? +/mob/living/simple_mob/updatehealth() + health = getMaxHealth() - getToxLoss() - getFireLoss() - getBruteLoss() + + //Alive, becoming dead + if((stat < DEAD) && (health <= 0)) + death() + + //Overhealth + if(health > getMaxHealth()) + health = getMaxHealth() + + //Update our hud if we have one + if(healths) + if(stat != DEAD) + var/heal_per = (health / getMaxHealth()) * 100 + switch(heal_per) + if(100 to INFINITY) + healths.icon_state = "health0" + if(80 to 100) + healths.icon_state = "health1" + if(60 to 80) + healths.icon_state = "health2" + if(40 to 60) + healths.icon_state = "health3" + if(20 to 40) + healths.icon_state = "health4" + if(0 to 20) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + else + healths.icon_state = "health7" + + //Updates the nutrition while we're here + if(nutrition_icon) + var/food_per = (nutrition / initial(nutrition)) * 100 + switch(food_per) + if(90 to INFINITY) + nutrition_icon.icon_state = "nutrition0" + if(75 to 90) + nutrition_icon.icon_state = "nutrition1" + if(50 to 75) + nutrition_icon.icon_state = "nutrition2" + if(25 to 50) + nutrition_icon.icon_state = "nutrition3" + if(0 to 25) + nutrition_icon.icon_state = "nutrition4" + +// Override for special bullshit. +/mob/living/simple_mob/proc/handle_special() + return + + +// Handle interacting with and taking damage from atmos +// TODO - Refactor this to use handle_environment() like a good /mob/living +/mob/living/simple_mob/proc/handle_atmos() + var/atmos_unsuitable = 0 + + var/atom/A = src.loc + + if(istype(A,/turf)) + var/turf/T = A + + var/datum/gas_mixture/Environment = T.return_air() + + if(Environment) + + if( abs(Environment.temperature - bodytemperature) > 40 ) + bodytemperature += ((Environment.temperature - bodytemperature) / 5) + + if(min_oxy) + if(Environment.gas["oxygen"] < min_oxy) + atmos_unsuitable = 1 + if(max_oxy) + if(Environment.gas["oxygen"] > max_oxy) + atmos_unsuitable = 1 + if(min_tox) + if(Environment.gas["phoron"] < min_tox) + atmos_unsuitable = 2 + if(max_tox) + if(Environment.gas["phoron"] > max_tox) + atmos_unsuitable = 2 + if(min_n2) + if(Environment.gas["nitrogen"] < min_n2) + atmos_unsuitable = 1 + if(max_n2) + if(Environment.gas["nitrogen"] > max_n2) + atmos_unsuitable = 1 + if(min_co2) + if(Environment.gas["carbon_dioxide"] < min_co2) + atmos_unsuitable = 1 + if(max_co2) + if(Environment.gas["carbon_dioxide"] > max_co2) + atmos_unsuitable = 1 + + //Atmos effect + if(bodytemperature < minbodytemp) + fire_alert = 2 + adjustBruteLoss(cold_damage_per_tick) + if(fire) + fire.icon_state = "fire1" + else if(bodytemperature > maxbodytemp) + fire_alert = 1 + adjustBruteLoss(heat_damage_per_tick) + if(fire) + fire.icon_state = "fire2" + else + fire_alert = 0 + if(fire) + fire.icon_state = "fire0" + + if(atmos_unsuitable) + adjustBruteLoss(unsuitable_atoms_damage) + if(oxygen) + oxygen.icon_state = "oxy1" + else if(oxygen) + if(oxygen) + oxygen.icon_state = "oxy0" + + +/mob/living/simple_mob/proc/handle_supernatural() + if(purge) + purge -= 1 + +/mob/living/simple_mob/death(gibbed, deathmessage = "dies!") + density = 0 //We don't block even if we did before + + if(has_eye_glow) + remove_eyes() + + if(loot_list.len) //Drop any loot + for(var/path in loot_list) + if(prob(loot_list[path])) + new path(get_turf(src)) + + spawn(3) //We'll update our icon in a sec + update_icon() + + return ..(gibbed,deathmessage) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/on_click.dm b/code/modules/mob/living/simple_mob/on_click.dm new file mode 100644 index 0000000000..be162bc230 --- /dev/null +++ b/code/modules/mob/living/simple_mob/on_click.dm @@ -0,0 +1,47 @@ +/* + Animals +*/ +/mob/living/simple_mob/UnarmedAttack(var/atom/A, var/proximity) + if(!(. = ..())) + return + +// setClickCooldown(get_attack_speed()) + + if(has_hands && istype(A,/obj) && a_intent != I_HURT) + var/obj/O = A + return O.attack_hand(src) + + switch(a_intent) + if(I_HELP) + if(isliving(A)) + custom_emote(1,"[pick(friendly)] [A]!") + + if(I_HURT) + if(prob(special_attack_prob)) + if(special_attack_min_range <= 1) + special_attack_target(A) + + else if(melee_damage_upper == 0 && istype(A,/mob/living)) + custom_emote(1,"[pick(friendly)] [A]!") + + else + attack_target(A) + + if(I_GRAB) + if(has_hands) + A.attack_hand(src) + + if(I_DISARM) + if(has_hands) + A.attack_hand(src) + +/mob/living/simple_mob/RangedAttack(var/atom/A) +// setClickCooldown(get_attack_speed()) + var/distance = get_dist(src, A) + + if(prob(special_attack_prob) && (distance >= special_attack_min_range) && (distance <= special_attack_max_range)) + special_attack_target() + return + + if(projectiletype) + shoot_target(A) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/simple_hud.dm b/code/modules/mob/living/simple_mob/simple_hud.dm new file mode 100644 index 0000000000..fe851648b4 --- /dev/null +++ b/code/modules/mob/living/simple_mob/simple_hud.dm @@ -0,0 +1,311 @@ +/mob/living/simple_mob/instantiate_hud(var/datum/hud/hud) + if(!client) + return //Why bother. + + var/ui_style = 'icons/mob/screen1_animal.dmi' + if(ui_icons) + ui_style = ui_icons + + var/ui_color = "#ffffff" + var/ui_alpha = 255 + + var/list/adding = list() + var/list/other = list() + var/list/hotkeybuttons = list() + var/list/slot_info = list() + + hud.adding = adding + hud.other = other + hud.hotkeybuttons = hotkeybuttons + + var/list/hud_elements = list() + var/obj/screen/using + var/obj/screen/inventory/inv_box + + var/has_hidden_gear + if(LAZYLEN(hud_gears)) + for(var/gear_slot in hud_gears) + inv_box = new /obj/screen/inventory() + inv_box.icon = ui_style + inv_box.color = ui_color + inv_box.alpha = ui_alpha + + var/list/slot_data = hud_gears[gear_slot] + inv_box.name = gear_slot + inv_box.screen_loc = slot_data["loc"] + inv_box.slot_id = slot_data["slot"] + inv_box.icon_state = slot_data["state"] + slot_info["[inv_box.slot_id]"] = inv_box.screen_loc + + if(slot_data["dir"]) + inv_box.set_dir(slot_data["dir"]) + + if(slot_data["toggle"]) + other += inv_box + has_hidden_gear = 1 + else + adding += inv_box + + if(has_hidden_gear) + using = new /obj/screen() + using.name = "toggle" + using.icon = ui_style + using.icon_state = "other" + using.screen_loc = ui_inventory + using.hud_layerise() + using.color = ui_color + using.alpha = ui_alpha + adding += using + + //Intent Backdrop + using = new /obj/screen() + using.name = "act_intent" + using.icon = ui_style + using.icon_state = "intent_"+a_intent + using.screen_loc = ui_acti + using.color = ui_color + using.alpha = ui_alpha + hud.adding += using + hud.action_intent = using + + hud_elements |= using + + //Small intent quarters + var/icon/ico + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),1,ico.Height()/2,ico.Width()/2,ico.Height()) + using = new /obj/screen() + using.name = I_HELP + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM //These sit on the intent box + hud.adding += using + hud.help_intent = using + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),ico.Width()/2,ico.Height()/2,ico.Width(),ico.Height()) + using = new /obj/screen() + using.name = I_DISARM + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM + hud.adding += using + hud.disarm_intent = using + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),ico.Width()/2,1,ico.Width(),ico.Height()/2) + using = new /obj/screen() + using.name = I_GRAB + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM + hud.adding += using + hud.grab_intent = using + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),1,1,ico.Width()/2,ico.Height()/2) + using = new /obj/screen() + using.name = I_HURT + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM + hud.adding += using + hud.hurt_intent = using + + //Move intent (walk/run) + using = new /obj/screen() + using.name = "mov_intent" + using.icon = ui_style + using.icon_state = (m_intent == "run" ? "running" : "walking") + using.screen_loc = ui_movi + using.color = ui_color + using.alpha = ui_alpha + hud.adding += using + hud.move_intent = using + + //Resist button + using = new /obj/screen() + using.name = "resist" + using.icon = ui_style + using.icon_state = "act_resist" + using.screen_loc = ui_pull_resist + using.color = ui_color + using.alpha = ui_alpha + hud.hotkeybuttons += using + + //Pull button + pullin = new /obj/screen() + pullin.icon = ui_style + pullin.icon_state = "pull0" + pullin.name = "pull" + pullin.screen_loc = ui_pull_resist + hud.hotkeybuttons += pullin + hud_elements |= pullin + + //Health status + healths = new /obj/screen() + healths.icon = ui_style + healths.icon_state = "health0" + healths.name = "health" + healths.screen_loc = ui_health + hud_elements |= healths + + //Oxygen dep icon + oxygen = new /obj/screen() + oxygen.icon = ui_style + oxygen.icon_state = "oxy0" + oxygen.name = "oxygen" + oxygen.screen_loc = ui_oxygen + hud_elements |= oxygen + + //Toxins present icon + toxin = new /obj/screen() + toxin.icon = ui_style + toxin.icon_state = "tox0" + toxin.name = "toxin" + toxin.screen_loc = ui_toxin + hud_elements |= toxin + + //Fire warning + fire = new /obj/screen() + fire.icon = ui_style + fire.icon_state = "fire0" + fire.name = "fire" + fire.screen_loc = ui_fire + hud_elements |= fire + + //Pressure warning + pressure = new /obj/screen() + pressure.icon = ui_style + pressure.icon_state = "pressure0" + pressure.name = "pressure" + pressure.screen_loc = ui_pressure + hud_elements |= pressure + + //Body temp warning + bodytemp = new /obj/screen() + bodytemp.icon = ui_style + bodytemp.icon_state = "temp0" + bodytemp.name = "body temperature" + bodytemp.screen_loc = ui_temp + hud_elements |= bodytemp + + //Nutrition status + nutrition_icon = new /obj/screen() + nutrition_icon.icon = ui_style + nutrition_icon.icon_state = "nutrition0" + nutrition_icon.name = "nutrition" + nutrition_icon.screen_loc = ui_nutrition + hud_elements |= nutrition_icon + + pain = new /obj/screen( null ) + + zone_sel = new /obj/screen/zone_sel( null ) + zone_sel.icon = ui_style + zone_sel.color = ui_color + zone_sel.alpha = ui_alpha + zone_sel.overlays.Cut() + zone_sel.overlays += image('icons/mob/zone_sel.dmi', "[zone_sel.selecting]") + hud_elements |= zone_sel + + //Hand things + if(has_hands) + //Drop button + using = new /obj/screen() + using.name = "drop" + using.icon = ui_style + using.icon_state = "act_drop" + using.screen_loc = ui_drop_throw + using.color = ui_color + using.alpha = ui_alpha + hud.hotkeybuttons += using + + //Equip detail + using = new /obj/screen() + using.name = "equip" + using.icon = ui_style + using.icon_state = "act_equip" + using.screen_loc = ui_equip + using.color = ui_color + using.alpha = ui_alpha + hud.adding += using + + //Hand slots themselves + inv_box = new /obj/screen/inventory/hand() + inv_box.hud = src + inv_box.name = "r_hand" + inv_box.icon = ui_style + inv_box.icon_state = "r_hand_inactive" + if(!hand) //This being 0 or null means the right hand is in use + inv_box.icon_state = "r_hand_active" + inv_box.screen_loc = ui_rhand + inv_box.slot_id = slot_r_hand + inv_box.color = ui_color + inv_box.alpha = ui_alpha + hud.r_hand_hud_object = inv_box + hud.adding += inv_box + slot_info["[slot_r_hand]"] = inv_box.screen_loc + + inv_box = new /obj/screen/inventory/hand() + inv_box.hud = src + inv_box.name = "l_hand" + inv_box.icon = ui_style + inv_box.icon_state = "l_hand_inactive" + if(hand) //This being 1 means the left hand is in use + inv_box.icon_state = "l_hand_active" + inv_box.screen_loc = ui_lhand + inv_box.slot_id = slot_l_hand + inv_box.color = ui_color + inv_box.alpha = ui_alpha + hud.l_hand_hud_object = inv_box + hud.adding += inv_box + slot_info["[slot_l_hand]"] = inv_box.screen_loc + + //Swaphand titlebar + using = new /obj/screen/inventory() + using.name = "hand" + using.icon = ui_style + using.icon_state = "hand1" + using.screen_loc = ui_swaphand1 + using.color = ui_color + using.alpha = ui_alpha + hud.adding += using + + using = new /obj/screen/inventory() + using.name = "hand" + using.icon = ui_style + using.icon_state = "hand2" + using.screen_loc = ui_swaphand2 + using.color = ui_color + using.alpha = ui_alpha + hud.adding += using + + //Throw button + throw_icon = new /obj/screen() + throw_icon.icon = ui_style + throw_icon.icon_state = "act_throw_off" + throw_icon.name = "throw" + throw_icon.screen_loc = ui_drop_throw + throw_icon.color = ui_color + throw_icon.alpha = ui_alpha + hud.hotkeybuttons += throw_icon + hud_elements |= throw_icon + + extra_huds(hud,ui_style,hud_elements) + + client.screen = list() + + client.screen += hud_elements + client.screen += adding + hotkeybuttons + client.screen += client.void + + return diff --git a/code/modules/mob/living/simple_mob/simple_mob.dm b/code/modules/mob/living/simple_mob/simple_mob.dm new file mode 100644 index 0000000000..cc38d0f6b4 --- /dev/null +++ b/code/modules/mob/living/simple_mob/simple_mob.dm @@ -0,0 +1,252 @@ +// Reorganized and somewhat cleaned up. +// AI code has been made into a datum, inside the AI module folder. + +/mob/living/simple_mob + name = "animal" + desc = "" + icon = 'icons/mob/animal.dmi' + health = 20 + maxHealth = 20 + + mob_bump_flag = SIMPLE_ANIMAL + mob_swap_flags = MONKEY|SLIME|HUMAN + mob_push_flags = MONKEY|SLIME|HUMAN + + var/tt_desc = "Uncataloged Life Form" //Tooltip description + + //Settings for played mobs + var/show_stat_health = 1 // Does the percentage health show in the stat panel for the mob + var/has_hands = 0 // Set to 1 to enable the use of hands and the hands hud + var/list/hud_gears // Slots to show on the hud (typically none) + var/ui_icons // Icon file path to use for the HUD, otherwise generic icons are used + var/r_hand_sprite // If they have hands, + var/l_hand_sprite // they could use some icons. + var/player_msg // Message to print to players about 'how' to play this mob on login. + + //Mob icon/appearance settings + var/icon_living = "" // The iconstate if we're alive, required + var/icon_dead = "" // The iconstate if we're dead, required + var/icon_gib = "generic_gib" // The iconstate for being gibbed, optional. Defaults to a generic gib animation. + var/icon_rest = null // The iconstate for resting, optional + var/image/modifier_overlay = null // Holds overlays from modifiers. + var/image/eye_layer = null // Holds the eye overlay. + var/has_eye_glow = FALSE // If true, adds an overlay over the lighting plane for [icon_state]-eyes. + attack_icon = 'icons/effects/effects.dmi' //Just the default, played like the weapon attack anim + attack_icon_state = "slash" //Just the default + + //Mob talking settings + universal_speak = 0 // Can all mobs in the entire universe understand this one? + var/has_langs = list(LANGUAGE_GALCOM)// Text name of their language if they speak something other than galcom. They speak the first one. + + //Movement things. + var/movement_cooldown = 5 // Lower is faster. + var/movement_sound = null // If set, will play this sound when it moves on its own will. + + //Mob interaction + var/response_help = "tries to help" // If clicked on help intent + var/response_disarm = "tries to disarm" // If clicked on disarm intent + var/response_harm = "tries to hurt" // If clicked on harm intent + var/list/friends = list() // Mobs on this list wont get attacked regardless of faction status. + var/harm_intent_damage = 3 // How much an unarmed harm click does to this mob. + var/meat_amount = 0 // How much meat to drop from this mob when butchered + var/obj/meat_type // The meat object to drop + var/list/loot_list = list() // The list of lootable objects to drop, with "/path = prob%" structure + var/obj/item/weapon/card/id/myid// An ID card if they have one to give them access to stuff. + + //Mob environment settings + var/minbodytemp = 250 // Minimum "okay" temperature in kelvin + var/maxbodytemp = 350 // Maximum of above + var/heat_damage_per_tick = 3 // Amount of damage applied if animal's body temperature is higher than maxbodytemp + var/cold_damage_per_tick = 2 // Same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + var/fire_alert = 0 // 0 = fine, 1 = hot, 2 = cold + + var/min_oxy = 5 // Oxygen in moles, minimum, 0 is 'no minimum' + var/max_oxy = 0 // Oxygen in moles, maximum, 0 is 'no maximum' + var/min_tox = 0 // Phoron min + var/max_tox = 1 // Phoron max + var/min_co2 = 0 // CO2 min + var/max_co2 = 5 // CO2 max + var/min_n2 = 0 // N2 min + var/max_n2 = 0 // N2 max + var/unsuitable_atoms_damage = 2 // This damage is taken when atmos doesn't fit all the requirements above + + //Hostility settings + var/taser_kill = 1 // Is the mob weak to tasers + + //Attack ranged settings + var/projectiletype // The projectiles I shoot + var/projectilesound // The sound I make when I do it + var/casingtype // What to make the hugely laggy casings pile out of + + //Mob melee settings + var/melee_damage_lower = 2 // Lower bound of randomized melee damage + var/melee_damage_upper = 6 // Upper bound of randomized melee damage + var/list/attacktext = list("attacked") // "You are [attacktext] by the mob!" + var/list/friendly = list("nuzzles") // "The mob [friendly] the person." + var/attack_sound = null // Sound to play when I attack + var/melee_miss_chance = 15 // percent chance to miss a melee attack. + var/attack_armor_type = "melee" // What armor does this check? + var/attack_armor_pen = 0 // How much armor pen this attack has. + var/attack_sharp = 0 // Is the attack sharp? + var/attack_edge = 0 // Does the attack have an edge? + + //Special attacks + var/special_attack_prob = 0 // Chance of the mob doing a special attack (0 for never) + var/special_attack_min_range = 0 // Min range to perform the special attacks from + var/special_attack_max_range = 0 // Max range to perform special attacks from + + //Damage resistances + var/grab_resist = 0 // Chance for a grab attempt to fail. Note that this is not a true resist and is just a prob() of failure. + var/resistance = 0 // Damage reduction for all types + var/list/armor = list( // Values for normal getarmor() checks + "melee" = 0, + "bullet" = 0, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + var/list/armor_soak = list( // Values for getsoak() checks. + "melee" = 0, + "bullet" = 0, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + var/purge = 0 // Cult stuff. + var/supernatural = FALSE // Ditto. + + +/mob/living/simple_mob/initialize() + verbs -= /mob/verb/observe + maxHealth = health + + for(var/L in has_langs) + languages |= all_languages[L] + if(languages.len) + default_language = languages[1] + + if(has_eye_glow) + add_eyes() + return ..() + + +/mob/living/simple_mob/Destroy() + default_language = null + if(myid) + qdel(myid) + myid = null + + friends.Cut() + languages.Cut() + + if(has_eye_glow) + remove_eyes() + return ..() + + +//Client attached +/mob/living/simple_mob/Login() + . = ..() + to_chat(src,"You are \the [src]. [player_msg]") + + +/mob/living/simple_mob/emote(var/act, var/type, var/desc) + if(act) + ..(act, type, desc) + + +/mob/living/simple_mob/SelfMove() + . = ..() + if(movement_sound) + playsound(src, movement_sound, 50, 1) + +/mob/living/simple_mob/movement_delay() + var/tally = 0 //Incase I need to add stuff other than "speed" later + + tally = movement_cooldown + + if(force_max_speed) + return -3 + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.haste) && M.haste == TRUE) + return -3 + if(!isnull(M.slowdown)) + tally += M.slowdown + + if(purge)//Purged creatures will move more slowly. The more time before their purge stops, the slower they'll move. + if(tally <= 0) + tally = 1 + tally *= purge + + if(m_intent == "walk") + tally *= 1.5 + + return tally+config.animal_delay + + +/mob/living/simple_mob/Stat() + ..() + if(statpanel("Status") && show_stat_health) + stat(null, "Health: [round((health / getMaxHealth()) * 100)]%") + +/mob/living/simple_mob/lay_down() + ..() + if(resting && icon_rest) + icon_state = icon_rest + else + icon_state = icon_living + update_icon() + + +/mob/living/simple_mob/say(var/message,var/datum/language/language) + var/verb = "says" + if(speak_emote.len) + verb = pick(speak_emote) + + message = sanitize(message) + + ..(message, null, verb) + +/mob/living/simple_mob/get_speech_ending(verb, var/ending) + return verb + + +/mob/living/simple_mob/put_in_hands(var/obj/item/W) // No hands. + W.forceMove(get_turf(src)) + return 1 + +// Harvest an animal's delicious byproducts +/mob/living/simple_mob/proc/harvest(var/mob/user) + var/actual_meat_amount = max(1,(meat_amount/2)) + if(meat_type && actual_meat_amount>0 && (stat == DEAD)) + for(var/i=0;i[user] chops up \the [src]!") + new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) + qdel(src) + else + user.visible_message("[user] butchers \the [src] messily!") + gib() + + +/mob/living/simple_mob/is_sentient() + return mob_class & MOB_CLASS_HUMANOID|MOB_CLASS_ANIMAL // Update this if needed. +// return intelligence_level != SA_PLANT && intelligence_level != SA_ROBOTIC + +//Just some subpaths for easy searching +/mob/living/simple_mob/hostile + faction = "not yours" +// ai_holder_type = /datum/ai_holder/regular/hostile + +/mob/living/simple_mob/retaliate +// ai_holder_type = /datum/ai_holder/regular/retaliate + +/mob/living/simple_mob/get_nametag_desc(mob/user) + return "[tt_desc]" \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/animal.dm b/code/modules/mob/living/simple_mob/subtypes/animal/animal.dm new file mode 100644 index 0000000000..864ee02ace --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/animal.dm @@ -0,0 +1,2 @@ +/mob/living/simple_mob/animal + mob_class = MOB_CLASS_ANIMAL \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider.dm new file mode 100644 index 0000000000..efa0dc77bc --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider.dm @@ -0,0 +1,64 @@ +/* + Spiders come in various types, and are a fairly common enemy both inside and outside the station. + Their attacks can inject reagents, which can cause harm long after the spider is killed. + Thick material will prevent injections, similar to other means of injections. +*/ + +// The base spider, in the 'walking tank' family. +/mob/living/simple_mob/animal/giant_spider + name = "giant spider" + desc = "Furry and brown, it makes you shudder to look at it. This one has deep red eyes." + tt_desc = "Atrax robustus gigantus" + icon_state = "guard" + icon_living = "guard" + icon_dead = "guard_dead" + has_eye_glow = TRUE + + faction = "spiders" + maxHealth = 200 + health = 200 + pass_flags = PASSTABLE + movement_cooldown = 10 + + see_in_dark = 10 + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "punches" + + melee_damage_lower = 18 + melee_damage_upper = 30 + attack_sharp = 1 + attack_edge = 1 + + heat_damage_per_tick = 20 + cold_damage_per_tick = 20 + + speak_emote = list("chitters") + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat/spidermeat + + say_list_type = /datum/say_list/spider + ai_holder_type = /datum/ai_holder/simple_mob/melee + + var/poison_type = "spidertoxin" // The reagent that gets injected when it attacks. + var/poison_chance = 10 // Chance for injection to occur. + var/poison_per_bite = 5 // Amount added per injection. + +/mob/living/simple_mob/animal/giant_spider/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.reagents) + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + inject_poison(L, target_zone) + +// Does actual poison injection, after all checks passed. +/mob/living/simple_mob/animal/giant_spider/proc/inject_poison(mob/living/L, target_zone) + if(prob(poison_chance)) + to_chat(L, "You feel a tiny prick.") + L.reagents.add_reagent(poison_type, poison_per_bite) + +// Subtype + + diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm new file mode 100644 index 0000000000..8d7532fbf5 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm @@ -0,0 +1,188 @@ +// Nurses, they create webs and eggs. +// They're fragile but their attacks can cause horrifying consequences. +/mob/living/simple_mob/animal/giant_spider/nurse + desc = "Furry and beige, it makes you shudder to look at it. This one has brilliant green eyes." + icon_state = "nurse" + icon_living = "nurse" + icon_dead = "nurse_dead" + + maxHealth = 40 + health = 40 + + movement_cooldown = 5 // A bit faster so that they can inject the eggs easier. + + melee_damage_lower = 5 // Doesn't do a lot of damage, since the goal is to make more spiders with egg attacks. + melee_damage_upper = 10 + poison_per_bite = 5 + poison_type = "stoxin" + + player_msg = "You can spin webs on an adjacent tile, or cocoon an object by clicking on it.
\ + You can also cocoon a dying or dead entity by clicking on them, and you will gain charges for egg-laying.
\ + To lay eggs, click a nearby tile. Laying eggs will deplete a charge." + ai_holder_type = /datum/ai_holder/simple_mob/melee/nurse_spider + + var/fed = 0 // Counter for how many egg laying 'charges' the spider has. + var/egg_inject_chance = 25 // One in four chance to get eggs. + var/egg_type = /obj/effect/spider/eggcluster/small + var/web_type = /obj/effect/spider/stickyweb/dark + + +/mob/living/simple_mob/animal/giant_spider/nurse/inject_poison(mob/living/L, target_zone) + ..() // Inject the stoxin here. + if(ishuman(L) && prob(egg_inject_chance)) + var/mob/living/carbon/human/H = L + var/obj/item/organ/external/O = H.get_organ(target_zone) + if(O) + var/eggcount = 0 + for(var/obj/effect/spider/eggcluster/E in O.implants) + eggcount++ + if(!eggcount) + var/eggs = new egg_type(O, src) + O.implants += eggs + to_chat(H, span("critical", "\The [src] injects something into your [O.name]!") ) // Oh god its laying eggs in me! + +// Webs target in a web if able to. +/mob/living/simple_mob/animal/giant_spider/nurse/attack_target(atom/A) + if(isturf(A)) + if(fed) + return lay_eggs(A) + return web_tile(A) + + if(isliving(A)) + var/mob/living/L = A + if(!L.stat) + return ..() + + if(!istype(A, /atom/movable)) + return + var/atom/movable/AM = A + + if(AM.anchored) + return ..() + + return spin_cocoon(AM) + +/mob/living/simple_mob/animal/giant_spider/nurse/proc/spin_cocoon(atom/movable/AM) + if(!istype(AM)) + return FALSE // We can't cocoon walls sadly. + visible_message(span("notice", "\The [src] begins to secrete a sticky substance around \the [AM].") ) + + // Get our AI to stay still. + set_AI_busy(TRUE) + + if(!do_mob(src, AM, 5 SECONDS)) + set_AI_busy(FALSE) + to_chat(src, span("warning", "You need to stay still to spin a web around \the [AM].")) + return FALSE + + set_AI_busy(FALSE) + + if(!AM) // Make sure it didn't get deleted for whatever reason. + to_chat(src, span("warning", "Whatever you were spinning a web for, its no longer there...")) + return FALSE + + if(!isturf(AM.loc)) + to_chat(src, span("warning", "You can't spin \the [AM] in a web while it is inside \the [AM.loc].")) + return FALSE + + if(!Adjacent(AM)) + to_chat(src, span("warning", "You need to be next to \the [AM] to spin it into a web.")) + return FALSE + + // Finally done with the checks. + var/obj/effect/spider/cocoon/C = new(AM.loc) + var/large_cocoon = FALSE + for(var/mob/living/L in C.loc) + if(istype(L, /mob/living/simple_mob/animal/giant_spider)) // Cannibalism is bad. + continue + fed++ + visible_message(span("warning","\The [src] sticks a proboscis into \the [L], and sucks a viscous substance out.")) + to_chat(src, span("notice", "You've fed upon \the [L], and can now lay [fed] cluster\s of eggs.")) + L.forceMove(C) + large_cocoon = TRUE + break + + // This part's pretty stupid. + for(var/obj/O in C.loc) + if(!O.anchored) + O.forceMove(C) + + // Todo: Put this code on the cocoon object itself? + if(large_cocoon) + C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") + + return TRUE + +/mob/living/simple_mob/animal/giant_spider/nurse/handle_special() + set waitfor = FALSE + if(get_AI_stance() == STANCE_IDLE && !is_AI_busy() && isturf(loc)) + if(fed) + lay_eggs(loc) + else + web_tile(loc) + +/mob/living/simple_mob/animal/giant_spider/nurse/proc/web_tile(turf/T) + if(!istype(T)) + return FALSE + + var/obj/effect/spider/stickyweb/W = locate() in T + if(W) + return FALSE // Already got webs here. + + visible_message(span("notice", "\The [src] begins to secrete a sticky substance.") ) + // Get our AI to stay still. + set_AI_busy(TRUE) + + if(!do_mob(src, T, 5 SECONDS)) + set_AI_busy(FALSE) + to_chat(src, span("warning", "You need to stay still to spin a web on \the [T].")) + return FALSE + + W = locate() in T + if(W) + return FALSE // Spamclick protection. + + set_AI_busy(FALSE) + new web_type(T) + return TRUE + + +/mob/living/simple_mob/animal/giant_spider/nurse/proc/lay_eggs(turf/T) + if(!istype(T)) + return FALSE + + if(!fed) + return FALSE + + var/obj/effect/spider/eggcluster/E = locate() in T + if(E) + return FALSE // Already got eggs here. + + visible_message(span("notice", "\The [src] begins to lay a cluster of eggs.") ) + // Get our AI to stay still. + set_AI_busy(TRUE) + + if(!do_mob(src, T, 5 SECONDS)) + set_AI_busy(FALSE) + to_chat(src, span("warning", "You need to stay still to lay eggs on \the [T].")) + return FALSE + + E = locate() in T + if(E) + return FALSE // Spamclick protection. + + set_AI_busy(FALSE) + new egg_type(T) + fed-- + return TRUE + + +// Variant that 'blocks' light (by being a negative light source). +// This is done to make webbed rooms scary and allow for spiders on the other side of webs to see prey. +/obj/effect/spider/stickyweb/dark + name = "dense web" + desc = "It's sticky, and blocks a lot of light." + light_color = "#FFFFFF" + light_range = 2 + light_power = -3 + diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm new file mode 100644 index 0000000000..fd0f5a8fbc --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm @@ -0,0 +1,59 @@ +/* + Hooligan Crabs are called so because they are rather curious and tend to follow people, + whether the people want them to or not, and sometimes causing vandalism by accident. + They're pretty strong and have strong melee armor, but won't attack first. + They unknowingly play a role in keeping the shoreline fairly safe, by killing whatever would attack other people. +*/ + +/mob/living/simple_mob/animal/sif/hooligan_crab + name = "hooligan crab" + desc = "A large, hard-shelled crustacean. This one is mostly grey." + icon_state = "sif_crab" + icon_living = "sif_crab" + icon_dead = "sif_crab_dead" + icon_scale = 1.5 + + faction = "crabs" + + maxHealth = 200 + health = 200 + movement_cooldown = 10 + movement_sound = 'sound/weapons/heavysmash.ogg' + armor = list( + "melee" = 40, + "bullet" = 20, + "laser" = 10, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + armor_soak = list( + "melee" = 10, + "bullet" = 5, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + mob_size = MOB_LARGE + + melee_damage_lower = 22 + melee_damage_upper = 35 + attack_armor_pen = 35 + attack_sharp = 1 + attack_edge = 1 + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + friendly = "pinches" + attacktext = list("clawed", "pinched", "crushed") + speak_emote = list("clicks") + + ai_holder_type = /datum/ai_holder/simple_mob/melee/hooligan + say_list_type = /datum/say_list/crab + diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/sif.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/sif.dm new file mode 100644 index 0000000000..b38949c237 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/sif.dm @@ -0,0 +1,3 @@ +// Mobs intended to be on Sif. As such, they won't die to the cold. +/mob/living/simple_mob/animal/sif + minbodytemp = 175 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm new file mode 100644 index 0000000000..bf425954a0 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm @@ -0,0 +1,71 @@ +/* + Combat drones have a rapid ranged attack, and have a projectile shield. + They are rather slow, but attempt to 'kite' its target. + A solid hit with an EMP grenade will kill the shield instantly. +*/ + +/mob/living/simple_mob/mechanical/combat_drone + name = "combat drone" + desc = "An automated combat drone armed with state of the art weaponry and shielding." + icon_state = "drone" + icon_living = "drone" + icon_dead = "drone_dead" + has_eye_glow = TRUE + + faction = "malf_drone" + + maxHealth = 50 // Shield has 150 for total of 200. + health = 50 + movement_cooldown = 5 + hovering = TRUE + + base_attack_cooldown = 5 + projectiletype = /obj/item/projectile/beam/drone + projectilesound = 'sound/weapons/laser3.ogg' + + response_help = "pokes" + response_disarm = "gently pushes aside" + response_harm = "hits" + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening + say_list_type = /datum/say_list/malf_drone + + var/datum/effect/effect/system/ion_trail_follow/ion_trail = null + var/obj/item/shield_projector/shields = null + +/mob/living/simple_mob/mechanical/combat_drone/initialize() + ion_trail = new + ion_trail.set_up(src) + ion_trail.start() + + shields = new /obj/item/shield_projector/rectangle/automatic/drone(src) + return ..() + +/mob/living/simple_mob/mechanical/combat_drone/Destroy() + qdel_null(ion_trail) + qdel_null(shields) + return ..() + +/mob/living/simple_mob/mechanical/combat_drone/death() + ..(null,"suddenly breaks apart.") + qdel(src) + +/mob/living/simple_mob/mechanical/combat_drone/Process_Spacemove(var/check_drift = 0) + return TRUE + +/obj/item/projectile/beam/drone + damage = 10 + +/obj/item/shield_projector/rectangle/automatic/drone + shield_health = 150 + max_shield_health = 150 + shield_regen_delay = 10 SECONDS + shield_regen_amount = 10 + size_x = 1 + size_y = 1 + +// A slightly easier drone, for POIs. +// Difference is that it should not be faster than you. +/mob/living/simple_mob/mechanical/combat_drone/lesser + desc = "An automated combat drone with an aged apperance." + movement_cooldown = 10 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm new file mode 100644 index 0000000000..7fd21b43c7 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm @@ -0,0 +1,20 @@ +/mob/living/simple_mob/mechanical + mob_class = MOB_CLASS_CONSTRUCT + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + taser_kill = FALSE + +/mob/living/simple_mob/mechanical/isSynthetic() + return TRUE + +/mob/living/simple_mob/mechanical/speech_bubble_appearance() + return faction != "neutral" ? "synthetic_evil" : "machine" \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm new file mode 100644 index 0000000000..e718cd880f --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm @@ -0,0 +1,36 @@ +/* + Viscerators are fragile and don't hit very hard, but fast, evasive, and rarely come alone. + They also tend to dodge while in melee range. + A weapon that can cleave is very effective against them. +*/ + +/mob/living/simple_mob/mechanical/viscerator + name = "viscerator" + desc = "A small, twin-bladed machine capable of inflicting very deadly lacerations." + icon = 'icons/mob/critter.dmi' + icon_state = "viscerator_attack" + icon_living = "viscerator_attack" + hovering = TRUE // Won't trigger landmines. + + faction = "syndicate" + maxHealth = 15 + health = 15 + movement_cooldown = 0 + + pass_flags = PASSTABLE + mob_swap_flags = 0 + mob_push_flags = 0 + + melee_damage_lower = 4 // Approx 8 DPS. + melee_damage_upper = 4 + base_attack_cooldown = 5 // Two attacks a second or so. + attack_sharp = 1 + attack_edge = 1 + attack_sound = 'sound/weapons/bladeslice.ogg' + attacktext = list("cut", "sliced") + + ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive + +/mob/living/simple_mob/mechanical/viscerator/death() + ..(null,"is smashed into pieces!") + qdel(src) \ No newline at end of file diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 2841003152..80fc03bae1 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -13,6 +13,11 @@ /mob/proc/setMoveCooldown(var/timeout) move_delay = max(world.time + timeout, move_delay) +/mob/proc/check_move_cooldown() + if(world.time < src.move_delay) + return FALSE // Need to wait more. + return TRUE + /client/North() ..() @@ -198,7 +203,8 @@ if(moving) return 0 - if(world.time < mob.move_delay) return + if(!mob.check_move_cooldown()) + return if(locate(/obj/effect/stop/, mob.loc)) for(var/obj/effect/stop/S in mob.loc) diff --git a/code/modules/shieldgen/directional_shield.dm b/code/modules/shieldgen/directional_shield.dm index 1892a3f3ad..91d3a008b5 100644 --- a/code/modules/shieldgen/directional_shield.dm +++ b/code/modules/shieldgen/directional_shield.dm @@ -88,6 +88,8 @@ but allow those projectiles to leave the shield from the inside. Blocking too many damaging projectiles will cause the shield to fail." icon = 'icons/obj/device.dmi' icon_state = "signmaker_sec" + light_range = 4 + light_power = 4 var/active = FALSE // If it's on. var/shield_health = 400 // How much damage the shield blocks before breaking. This is a shared health pool for all shields attached to this projector. var/max_shield_health = 400 // Ditto. This is fairly high, but shields are really big, you can't miss them, and laser carbines pump out so much hurt. @@ -170,6 +172,8 @@ var/new_color = rgb(new_r, new_g, new_b) + set_light(light_range, light_power, new_color) + // Now deploy the new color to all the shields. for(var/obj/effect/directional_shield/S in active_shields) S.update_color(new_color) diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi index 90cc743bcff3c305d3f74408422ac82a4df220de..a88deb1b5de770f931fd57ff6963b5f9d3027c01 100644 GIT binary patch delta 55707 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

D(hF$ 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%0ANID3kgsZM&$=|DdhYm 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 diff --git a/polaris.dme b/polaris.dme index efd6fe7878..2064763b89 100644 --- a/polaris.dme +++ b/polaris.dme @@ -1266,6 +1266,7 @@ #include "code\modules\ai\ai_holder_targeting.dm" #include "code\modules\ai\interfaces.dm" #include "code\modules\ai\say_list.dm" +#include "code\modules\ai\aI_holder_subtypes\simple_mob_ai.dm" #include "code\modules\alarm\alarm.dm" #include "code\modules\alarm\alarm_handler.dm" #include "code\modules\alarm\atmosphere_alarm.dm" @@ -1887,7 +1888,6 @@ #include "code\modules\mob\living\silicon\robot\subtypes\syndicate.dm" #include "code\modules\mob\living\simple_animal\corpse.dm" #include "code\modules\mob\living\simple_animal\simple_animal.dm" -#include "code\modules\mob\living\simple_animal\simple_animal2.dm" #include "code\modules\mob\living\simple_animal\simple_hud.dm" #include "code\modules\mob\living\simple_animal\aliens\alien.dm" #include "code\modules\mob\living\simple_animal\aliens\creature.dm" @@ -1937,6 +1937,22 @@ #include "code\modules\mob\living\simple_animal\slime\life.dm" #include "code\modules\mob\living\simple_animal\slime\slime.dm" #include "code\modules\mob\living\simple_animal\slime\subtypes.dm" +#include "code\modules\mob\living\simple_mob\appearance.dm" +#include "code\modules\mob\living\simple_mob\combat.dm" +#include "code\modules\mob\living\simple_mob\defense.dm" +#include "code\modules\mob\living\simple_mob\hands.dm" +#include "code\modules\mob\living\simple_mob\life.dm" +#include "code\modules\mob\living\simple_mob\on_click.dm" +#include "code\modules\mob\living\simple_mob\simple_hud.dm" +#include "code\modules\mob\living\simple_mob\simple_mob.dm" +#include "code\modules\mob\living\simple_mob\subtypes\animal\animal.dm" +#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider.dm" +#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\nurse.dm" +#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\hooligan_crab.dm" +#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\sif.dm" +#include "code\modules\mob\living\simple_mob\subtypes\mechanical\combat_drone.dm" +#include "code\modules\mob\living\simple_mob\subtypes\mechanical\mechanical.dm" +#include "code\modules\mob\living\simple_mob\subtypes\mechanical\viscerator.dm" #include "code\modules\mob\living\voice\voice.dm" #include "code\modules\mob\new_player\login.dm" #include "code\modules\mob\new_player\logout.dm" @@ -2448,7 +2464,7 @@ #include "code\ZAS\Zone.dm" #include "interface\interface.dm" #include "interface\skin.dmf" -#include "maps\plane\plane.dm" +#include "maps\example\example.dm" #include "maps\submaps\_readme.dm" #include "maps\submaps\space_submaps\space.dm" #include "maps\submaps\surface_submaps\mountains\mountains.dm"