diff --git a/code/modules/mob/living/simple_animal/friendly/fox_vr.dm b/code/modules/mob/living/simple_animal/friendly/fox_vr.dm new file mode 100644 index 00000000000..a6c0065a287 --- /dev/null +++ b/code/modules/mob/living/simple_animal/friendly/fox_vr.dm @@ -0,0 +1,308 @@ +/mob/living/simple_animal/fox + name = "Fox" + desc = "It's a fox. I wonder what it says?" + icon = 'icons/mob/fox_vr.dmi' + icon_state = "fox" + icon_living = "fox" + icon_dead = "fox_dead" + speak = list("Ack-Ack","Ack-Ack-Ack-Ackawoooo","Awoo","Tchoff") + speak_emote = list("geckers", "barks") + emote_hear = list("howls","barks") + emote_see = list("shakes its head", "shivers", "geckers") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/fox + response_help = "scritches" + response_disarm = "gently pushes aside" + response_harm = "kicks" + var/turns_since_scan = 0 + var/mob/living/simple_animal/mouse/movement_target + var/mob/flee_target + min_oxy = 16 //Require atleast 16kPA oxygen + minbodytemp = 223 //Below -50 Degrees Celcius + maxbodytemp = 323 //Above 50 Degrees Celcius + mob_size = MOB_TINY + +/mob/living/simple_animal/fox/New() + if(!vore_organs.len) + var/datum/belly/B = new /datum/belly(src) + B.immutable = 1 + B.name = "Stomach" + B.inside_flavor = "Slick foxguts. Cute on the outside, slimy on the inside!" + B.human_prey_swallow_time = swallowTime + B.nonhuman_prey_swallow_time = swallowTime + vore_organs[B.name] = B + vore_selected = B.name + + B.emote_lists[DM_HOLD] = list( + "The foxguts knead and churn around you harmlessly.", + "With a loud glorp, some air shifts inside the belly.", + "A thick drop of warm bellyslime drips onto you from above.", + "The fox turns suddenly, causing you to shift a little.", + "During a moment of relative silence, you can hear the fox breathing.", + "The slimey stomach walls squeeze you lightly, then relax.") + + B.emote_lists[DM_DIGEST] = list( + "The guts knead at you, trying to work you into thick soup.", + "You're ground on by the slimey walls, treated like a mouse.", + "The acrid air is hard to breathe, and stings at your lungs.", + "You can feel the acids coating you, ground in by the slick walls.", + "The fox's stomach churns hungrily over your form, trying to take you.", + "With a loud glorp, the stomach spills more acids onto you.") + ..() + +// All them complicated fox procedures. +/mob/living/simple_animal/fox/Life() + //MICE! + if((loc) && isturf(loc)) + if(!stat && !resting && !buckled) + for(var/mob/living/simple_animal/mouse/M in loc) + if(isPredator) //If the fox is a predator, + movement_target = null + custom_emote(1, "greedily stuffs [M] into their gaping maw!") + if(M in oview(1, src)) + animal_nom(M) + else + M << "You just manage to slip away from [src]'s jaws before you can be sent to a fleshy prison!" + break + else + if(!M.stat) + M.splat() + visible_emote(pick("bites \the [M]!","toys with \the [M].","chomps on \the [M]!")) + movement_target = null + stop_automated_movement = 0 + break + ..() + + for(var/mob/living/simple_animal/mouse/snack in oview(src,5)) + if(snack.stat < DEAD && prob(15)) + audible_emote(pick("hunkers down!","acts stealthy!","eyes [snack] hungrily.")) + break + + if(!stat && !resting && !buckled) //SEE A MICRO AND ARE A PREDATOR, EAT IT! + for(var/mob/living/carbon/human/food in oview(src, 5)) + + if(food.size_multiplier <= RESIZE_A_SMALLTINY) + if(prob(10)) + custom_emote(1, pick("eyes [food] hungrily!","licks their lips and turns towards [food] a little!","pants as they imagine [food] being in their belly.")) + break + else + if(prob(5)) + movement_target = food + break + + for(var/mob/living/carbon/human/bellyfiller in oview(1, src)) + if(bellyfiller in src.prey_excludes) + continue + + if(bellyfiller.size_multiplier <= RESIZE_A_SMALLTINY && isPredator) + movement_target = null + custom_emote(1, pick("slurps [bellyfiller] with their slimey tongue.","looms over [bellyfiller] with their maw agape.","sniffs at [bellyfiller], their belly grumbling hungrily.")) + sleep(10) + custom_emote(1, "starts to scoop [bellyfiller] into their maw!") + if(bellyfiller in oview(1, src)) + animal_nom(bellyfiller) + else + bellyfiller << "You just manage to slip away from [src]'s jaws before you can be sent to a fleshy prison!" + break + + if(!stat && !resting && !buckled) + turns_since_scan++ + if (turns_since_scan > 5) + walk_to(src,0) + turns_since_scan = 0 + + if (flee_target) //fleeing takes precendence + handle_flee_target() + else + handle_movement_target() + +/mob/living/simple_animal/fox/proc/handle_movement_target() + //if our target is neither inside a turf or inside a human(???), stop + if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) + movement_target = null + stop_automated_movement = 0 + //if we have no target or our current one is out of sight/too far away + if( !movement_target || !(movement_target.loc in oview(src, 4)) ) + movement_target = null + stop_automated_movement = 0 + for(var/mob/living/simple_animal/mouse/snack in oview(src)) //search for a new target + if(isturf(snack.loc) && !snack.stat) + movement_target = snack + break + + if(movement_target) + stop_automated_movement = 1 + walk_to(src,movement_target,0,10) + +/mob/living/simple_animal/fox/proc/handle_flee_target() + //see if we should stop fleeing + if (flee_target && !(flee_target.loc in view(src))) + flee_target = null + stop_automated_movement = 0 + + if (flee_target) + if(prob(25)) say("GRRRRR!") + stop_automated_movement = 1 + walk_away(src, flee_target, 7, 2) + +/mob/living/simple_animal/fox/proc/set_flee_target(atom/A) + if(A) + flee_target = A + turns_since_scan = 5 + +/mob/living/simple_animal/fox/attackby(var/obj/item/O, var/mob/user) + . = ..() + if(O.force) + set_flee_target(user? user : src.loc) + + +/mob/living/simple_animal/fox/attack_hand(mob/living/carbon/human/M as mob) + . = ..() + if(M.a_intent == "hurt") + set_flee_target(M) + +/mob/living/simple_animal/fox/ex_act() + . = ..() + set_flee_target(src.loc) + +/mob/living/simple_animal/fox/bullet_act(var/obj/item/projectile/proj) + . = ..() + set_flee_target(proj.firer? proj.firer : src.loc) + +/mob/living/simple_animal/fox/hitby(atom/movable/AM) + . = ..() + set_flee_target(AM.thrower? AM.thrower : src.loc) + +/mob/living/simple_animal/fox/MouseDrop(atom/over_object) + + var/mob/living/carbon/H = over_object + if(!istype(H) || !Adjacent(H)) return ..() + + if(H.a_intent == "help") + get_scooped(H) + return + else + return ..() + +/mob/living/simple_animal/fox/get_scooped(var/mob/living/carbon/grabber) + if (stat >= DEAD) + return //since the holder icon looks like a living cat + ..() + +//Basic friend AI +/mob/living/simple_animal/fox/fluff + var/mob/living/carbon/human/friend + var/befriend_job = null + +/mob/living/simple_animal/fox/fluff/handle_movement_target() + if (friend) + var/follow_dist = 5 + if (friend.stat >= DEAD || friend.health <= config.health_threshold_softcrit) //danger + follow_dist = 1 + else if (friend.stat || friend.health <= 50) //danger or just sleeping + follow_dist = 2 + var/near_dist = max(follow_dist - 2, 1) + var/current_dist = get_dist(src, friend) + + if (movement_target != friend) + if (current_dist > follow_dist && !istype(movement_target, /mob/living/simple_animal/mouse) && (friend in oview(src))) + //stop existing movement + walk_to(src,0) + turns_since_scan = 0 + + //walk to friend + stop_automated_movement = 1 + movement_target = friend + walk_to(src, movement_target, near_dist, 4) + + //already following and close enough, stop + else if (current_dist <= near_dist) + walk_to(src,0) + movement_target = null + stop_automated_movement = 0 + if (prob(10)) + say("Yap!") + + if (!friend || movement_target != friend) + ..() + +/mob/living/simple_animal/fox/fluff/Life() + ..() + if (stat || !friend) + return + if (get_dist(src, friend) <= 1) + if (friend.stat >= DEAD || friend.health <= config.health_threshold_softcrit) + if (prob((friend.stat < DEAD)? 50 : 15)) + var/verb = pick("huffs", "whines", "yowls") + audible_emote(pick("[verb] in distress.", "[verb] anxiously.")) + else + if (prob(5)) + visible_emote(pick("nips [friend] affectionately.", + "yaps at [friend].", + "looks at [friend], wagging.", + "wags happily.")) + else if (friend.health <= 50) + if (prob(10)) + var/verb = pick("huffs", "whines", "yowls") + audible_emote("[verb] anxiously.") + +/mob/living/simple_animal/fox/fluff/verb/friend() + set name = "Become Friends" + set category = "IC" + set src in view(1) + + if(friend && usr == friend) + set_dir(get_dir(src, friend)) + say("Yap!") + return + + if (!(ishuman(usr) && befriend_job && usr.job == befriend_job)) + usr << "[src] ignores you." + return + + friend = usr + + set_dir(get_dir(src, friend)) + say("Yap!") + +/obj/item/weapon/reagent_containers/food/snacks/meat/fox + name = "Fox meat" + desc = "The fox doesn't say a god damn thing, now." + +//Captain fox +/mob/living/simple_animal/fox/fluff/Renault + name = "Renault" + desc = "Renault, the Captain's trustworthy fox. I wonder what it says?" + isPredator = 1 + befriend_job = "Captain" + +/mob/living/simple_animal/fox/fluff/Renault/New() + if(!vore_organs.len) + var/datum/belly/B = new /datum/belly(src) + B.immutable = 1 + B.name = "Stomach" + B.inside_flavor = "Slick foxguts. They seem somehow more regal than perhaps other foxes!" + B.human_prey_swallow_time = swallowTime + B.nonhuman_prey_swallow_time = swallowTime + vore_organs[B.name] = B + vore_selected = B.name + + B.emote_lists[DM_HOLD] = list( + "Renault's stomach walls squeeze around you more tightly for a moment, before relaxing, as if testing you a bit.", + "There's a sudden squeezing as Renault presses a forepaw against his gut over you, squeezng you against the slick walls.", + "The 'head fox' has a stomach that seems to think you belong to it. It might be hard to argue, as it kneads at your form.", + "If being in the captain's fox is a promotion, it might not feel like one. The belly just coats you with more thick foxslime.", + "It doesn't seem like Renault wants to let you out. The stomach and owner possessively squeeze around you.", + "Renault's stomach walls squeeze closer, as he belches quietly, before swallowing more air. Does he do that on purpose?") + + B.emote_lists[DM_DIGEST] = list( + "Renault's stomach walls grind hungrily inwards, kneading acids against your form, and treating you like any other food.", + "The captain's fox impatiently kneads and works acids against you, trying to claim your body for fuel.", + "The walls knead in firmly, squeezing and tossing you around briefly in disorienting aggression.", + "Renault belches, letting the remaining air grow more acrid. It burns your lungs with each breath.", + "A thick glob of acids drip down from above, adding to the pool of caustic fluids in Renault's belly.", + "There's a loud gurgle as the stomach declares the intent to make you a part of Renault.") + + ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/snake_vr.dm b/code/modules/mob/living/simple_animal/friendly/snake_vr.dm new file mode 100644 index 00000000000..f53c8f46483 --- /dev/null +++ b/code/modules/mob/living/simple_animal/friendly/snake_vr.dm @@ -0,0 +1,55 @@ +/mob/living/simple_animal/snake + name = "Snake" + desc = "A big thick snake." + icon = 'icons/mob/snake_vr.dmi' + icon_state = "snake" + icon_living = "snake" + icon_dead = "snake_dead" + speak_emote = list("hisses") + health = 20 + maxHealth = 20 + attacktext = "bites" + melee_damage_lower = 3 + melee_damage_upper = 5 + response_help = "pets" + response_disarm = "shoos" + response_harm = "kicks" + turns_per_move = 8 // SLOW-ASS MUTHAFUCKA + +//NOODLE IS HERE! SQUEEEEEEEE~ +/mob/living/simple_animal/snake/Noodle + name = "Noodle" + desc = "This snake is particularly chubby and demands nothing but the finest of treats." + isPredator = 1 + +//Special snek-snax for Noodle! +obj/item/weapon/reagent_containers/food/snacks/snakesnack + name = "Sugar mouse" + desc = "A little mouse treat made of coloured sugar. Noodle loves these!" + var/snack_colour + icon = 'icons/mob/snake_vr.dmi' + icon_state = "snack_yellow" + +obj/item/weapon/reagent_containers/food/snacks/snakesnack/New() + ..() + if(!snack_colour) + snack_colour = pick( list("yellow","green","pink","blue") ) + icon_state = "snack_[snack_colour]" + desc = "A little mouse treat made of coloured sugar. Noodle loves these! This one is [snack_colour]." + +obj/item/weapon/storage/box/fluff/snakesnackbox + name = "Box of Snake Snax" + desc = "A box containing Noodle's special sugermouse treats." + icon = 'icons/mob/snake_vr.dmi' + icon_state = "sneksnakbox" + storage_slots = 7 + New() + new /obj/item/weapon/reagent_containers/food/snacks/snakesnack(src) + new /obj/item/weapon/reagent_containers/food/snacks/snakesnack(src) + new /obj/item/weapon/reagent_containers/food/snacks/snakesnack(src) + new /obj/item/weapon/reagent_containers/food/snacks/snakesnack(src) + new /obj/item/weapon/reagent_containers/food/snacks/snakesnack(src) + new /obj/item/weapon/reagent_containers/food/snacks/snakesnack(src) + new /obj/item/weapon/reagent_containers/food/snacks/snakesnack(src) + ..() + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 33fb8acd248..10908e074bc 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -106,7 +106,7 @@ /mob/living/simple_animal/hostile/proc/ListTargets(var/dist = 7) - var/list/L = hearers(src, dist) + var/list/L = view(src, dist) //hearers(src, dist) <- This shit was broken and bad and caused a lot of lag because mobs would chase targets they can't see. -Spades for (var/obj/mecha/M in mechas_list) if (M.z == src.z && get_dist(src, M) <= dist) diff --git a/code/modules/mob/living/simple_animal/hostile/vore/alien.dm b/code/modules/mob/living/simple_animal/hostile/vore/alien.dm new file mode 100644 index 00000000000..ccb27f5b6e1 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/vore/alien.dm @@ -0,0 +1,95 @@ +/mob/living/simple_animal/hostile/vore/alien + name = "alien hunter" + desc = "Hiss!" + icon = 'icons/mob/alien.dmi' + icon_state = "xenohunter" + icon_living = "xenohunter" + icon_dead = "xenohunter-dead" + icon_gib = "gibbed-a" + response_help = "pokes" + response_disarm = "shoves" + response_harm = "hits" + speed = -1 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat + maxHealth = 100 + health = 100 + harm_intent_damage = 5 + melee_damage_lower = 25 + melee_damage_upper = 25 + attacktext = "slashed" + a_intent = "harm" + attack_sound = 'sound/weapons/bladeslice.ogg' + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + unsuitable_atoms_damage = 15 + faction = "alien" + environment_smash = 1 + status_flags = CANPUSH + minbodytemp = 0 + heat_damage_per_tick = 20 + capacity = 1 + + +/mob/living/simple_animal/hostile/vore/alien/drone + name = "alien drone" + icon_state = "xenodrone" + icon_living = "xenodrone" + icon_dead = "xenodrone-dead" + health = 60 + melee_damage_lower = 15 + melee_damage_upper = 15 + +/mob/living/simple_animal/hostile/vore/alien/sentinel + name = "alien sentinel" + icon_state = "xenosentinel" + icon_living = "xenosentinel" + icon_dead = "xenosentinel-dead" + health = 120 + melee_damage_lower = 15 + melee_damage_upper = 15 + ranged = 1 + projectiletype = /obj/item/projectile/neurotox + projectilesound = 'sound/weapons/pierce.ogg' + + +/mob/living/simple_animal/hostile/vore/alien/queen + name = "alien queen" + icon_state = "xenoqueen" + icon_living = "xenoqueen" + icon_dead = "xenoqueen-dead" + maxHealth = 250 + melee_damage_lower = 15 + melee_damage_upper = 15 + ranged = 1 + move_to_delay = 3 + projectiletype = /obj/item/projectile/neurotox + projectilesound = 'sound/weapons/pierce.ogg' + rapid = 1 + status_flags = 0 + +/mob/living/simple_animal/hostile/vore/alien/queen/large + name = "alien empress" + icon = 'icons/mob/alienqueen.dmi' + icon_state = "queen_s" + icon_living = "queen_s" + icon_dead = "queen_dead" + move_to_delay = 4 + maxHealth = 400 + health = 400 + pixel_x = -16 + capacity = 3 + +/obj/item/projectile/neurotox + damage = 30 + icon_state = "toxin" + +/mob/living/simple_animal/hostile/vore/alien/death() + ..() + visible_message("[src] lets out a waning guttural screech, green blood bubbling from its maw...") + playsound(src, 'sound/voice/hiss6.ogg', 100, 1) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/vore/bear.dm b/code/modules/mob/living/simple_animal/hostile/vore/bear.dm new file mode 100644 index 00000000000..d843351d69c --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/vore/bear.dm @@ -0,0 +1,170 @@ +//Space bears! +/mob/living/simple_animal/hostile/vore/bear + name = "space bear" + desc = "RawrRawr!!" + icon_state = "spacebear" + icon_living = "spacebear" + icon_dead = "spacebear-dead" + icon_gib = "bear-gib" + speak = list("RAWR!","Rawr!","GRR!","Growl!") + speak_emote = list("growls", "roars") + emote_hear = list("rawrs","grumbles","grawls") + emote_see = list("stares ferociously", "stomps") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/bearmeat + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "pokes" + stop_automated_movement_when_pulled = 0 + maxHealth = 60 + health = 60 + melee_damage_lower = 20 + melee_damage_upper = 30 + + //Space bears aren't affected by atmos. + 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 + var/stance_step = 0 + + faction = "russian" + +/mob/living/simple_animal/hostile/vore/bear/brown + name = "space bear" + desc = "RawrRawr!!" + icon_state = "brownbear" + icon_living = "brownbear" + icon_dead = "brownbear-dead" + icon_gib = "bear-gib" + +//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! +/mob/living/simple_animal/hostile/vore/bear/Hudson + name = "Hudson" + desc = "" + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "pokes" + +/mob/living/simple_animal/hostile/vore/bear/Life() + . =..() + if(!.) + return +/* + if(loc && istype(loc,/turf/space)) + icon_state = "bear" + else + icon_state = "bearfloor" +*/ + switch(stance) + + if(HOSTILE_STANCE_TIRED) + stop_automated_movement = 1 + stance_step++ + if(stance_step >= 10) //rests for 10 ticks + if(target_mob && target_mob in ListTargets(10)) + stance = HOSTILE_STANCE_ATTACK //If the mob he was chasing is still nearby, resume the attack, otherwise go idle. + else + stance = HOSTILE_STANCE_IDLE + + if(HOSTILE_STANCE_ALERT) + stop_automated_movement = 1 + var/found_mob = 0 + if(target_mob && target_mob in ListTargets(10)) + if(!(SA_attackable(target_mob))) + stance_step = max(0, stance_step) //If we have not seen a mob in a while, the stance_step will be negative, we need to reset it to 0 as soon as we see a mob again. + stance_step++ + found_mob = 1 + src.set_dir(get_dir(src,target_mob)) //Keep staring at the mob + + if(stance_step in list(1,4,7)) //every 3 ticks + var/action = pick( list( "growls at [target_mob]", "stares angrily at [target_mob]", "prepares to attack [target_mob]", "closely watches [target_mob]" ) ) + if(action) + custom_emote(1,action) + if(!found_mob) + stance_step-- + + if(stance_step <= -20) //If we have not found a mob for 20-ish ticks, revert to idle mode + stance = HOSTILE_STANCE_IDLE + if(stance_step >= 7) //If we have been staring at a mob for 7 ticks, + stance = HOSTILE_STANCE_ATTACK + + if(HOSTILE_STANCE_ATTACKING) + if(stance_step >= 20) //attacks for 20 ticks, then it gets tired and needs to rest + custom_emote(1, "is worn out and needs to rest." ) + stance = HOSTILE_STANCE_TIRED + stance_step = 0 + walk(src, 0) //This stops the bear's walking + return + + + +/mob/living/simple_animal/hostile/vore/bear/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING) + stance = HOSTILE_STANCE_ALERT + stance_step = 6 + target_mob = user + ..() + +/mob/living/simple_animal/hostile/vore/bear/attack_hand(mob/living/carbon/human/M as mob) + if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING) + stance = HOSTILE_STANCE_ALERT + stance_step = 6 + target_mob = M + ..() +/* +/mob/living/simple_animal/hostile/vore/bear/Process_Spacemove(var/check_drift = 0) + return //No drifting in space for space bears! +*/ +/mob/living/simple_animal/hostile/vore/bear/FindTarget() // TODO: Make it so if the target is laying down, the bear actually won't bother them. + . = ..() + if(.) + custom_emote(1,"stares alertly at [.]") + stance = HOSTILE_STANCE_ALERT + +/mob/living/simple_animal/hostile/vore/bear/LoseTarget() + ..(5) + +/mob/living/simple_animal/hostile/vore/bear/AttackingTarget() + + // Need to snowflake the vode in the bear code here because of the snowflakey way bears work. + if(isliving(target_mob.loc)) //They're inside a mob, maybe us, ignore! + return + + if(!isliving(target_mob)) //Can't eat 'em if they ain't alive. Prevents eating borgs/bots. + ..() + return + + if(picky && target_mob.digestable && target_mob.lying && target_mob.size_multiplier >= min_size && target_mob.size_multiplier <= max_size && !(target_mob in prey_exclusions)) + if(capacity) + var/check_size = target_mob.size_multiplier + fullness + if(check_size <= capacity) + animal_nom(target_mob) + else + animal_nom(target_mob) + return + + // Original bear attack code. + if(!Adjacent(target_mob)) + return + custom_emote(1, pick( list("slashes at [target_mob]", "bites [target_mob]") ) ) + + var/damage = rand(20,30) + + if(ishuman(target_mob)) + var/mob/living/carbon/human/H = target_mob + var/dam_zone = pick(BP_TORSO, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG) + var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone)) + H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp=1, edge=1) + return H + else if(isliving(target_mob)) + var/mob/living/L = target_mob + L.adjustBruteLoss(damage) + return L \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/vore/carp.dm b/code/modules/mob/living/simple_animal/hostile/vore/carp.dm new file mode 100644 index 00000000000..f40938c5185 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/vore/carp.dm @@ -0,0 +1,86 @@ +/mob/living/simple_animal/hostile/vore/carp + name = "space carp" + desc = "A voracious, fang-bearing creature that resembles a fish." + icon = 'icons/mob/animal.dmi' + icon_state = "carp" + icon_living = "carp" + icon_dead = "carp_dead" + icon_gib = "carp_gib" + speak_chance = 0 + turns_per_move = 5 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + speed = 4 + maxHealth = 25 + health = 25 + + harm_intent_damage = 8 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "bitten" + attack_sound = 'sound/weapons/bite.ogg' + + capacity = 1 + max_size = 0.5 + + //Space carp aren't affected by atmos. + 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 + + break_stuff_probability = 15 + + faction = "carp" + +/mob/living/simple_animal/hostile/vore/carp/Process_Spacemove(var/check_drift = 0) + return 1 //No drifting in space for space carp! //original comments do not steal + +/mob/living/simple_animal/hostile/vore/carp/FindTarget() + . = ..() + if(.) + custom_emote(1,"nashes at [.]") + +/mob/living/simple_animal/hostile/vore/carp/AttackingTarget() + . =..() + var/mob/living/L = . + if(istype(L)) + if(prob(15)) + L.Weaken(3) + L.visible_message("\the [src] knocks down \the [L]!") + + +// ---------- Great White Carp ---------- // +/mob/living/simple_animal/hostile/vore/large/carp + name = "great white carp" + desc = "You're going to need a bigger ship." + icon_dead = "megacarp-dead" + icon_living = "megacarp" + icon_state = "megacarp" + pixel_y = -16 // Center this completely since it's in space. + maxHealth = 600 // Boss + health = 600 + speed = 3 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat + meat_amount = 10 + faction = "carp" + capacity = 2 + max_size = 2 + //Space carp aren't affected by atmos. + 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 + break_stuff_probability = 15 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/vore/creature.dm b/code/modules/mob/living/simple_animal/hostile/vore/creature.dm new file mode 100644 index 00000000000..59f52cd5ed0 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/vore/creature.dm @@ -0,0 +1,19 @@ +/mob/living/simple_animal/hostile/vore/creature + name = "creature" + desc = "A sanity-destroying otherthing." + icon = 'icons/mob/critter.dmi' + speak_emote = list("gibbers") + icon_state = "otherthing" + icon_living = "otherthing" + icon_dead = "otherthing-dead" + health = 80 + maxHealth = 80 + melee_damage_lower = 25 + melee_damage_upper = 50 + attacktext = "chomped" + attack_sound = 'sound/weapons/bite.ogg' + faction = "creature" + speed = 4 + capacity = 0 + max_size = 2 // Max: 2 + min_size = 0.25 // Min: 0.25 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/vore/mimic.dm b/code/modules/mob/living/simple_animal/hostile/vore/mimic.dm new file mode 100644 index 00000000000..1a765b49d14 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/vore/mimic.dm @@ -0,0 +1,197 @@ +// +// Abstract Class +// +/mob/living/simple_animal/hostile/vore/mimic + name = "crate" + desc = "A rectangular steel crate." + icon = 'icons/obj/storage.dmi' + icon_state = "crate" + icon_living = "crate" + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat + response_help = "touches" + response_disarm = "pushes" + response_harm = "hits" + speed = 4 + maxHealth = 250 + health = 250 + + harm_intent_damage = 5 + melee_damage_lower = 8 + melee_damage_upper = 12 + attacktext = "attacked" + attack_sound = 'sound/weapons/bite.ogg' + + 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 + + capacity = 0 + + faction = "mimic" + move_to_delay = 8 + +/mob/living/simple_animal/hostile/vore/mimic/FindTarget() + . = ..() + if(.) + audible_emote("growls at [.]") + +/mob/living/simple_animal/hostile/vore/mimic/death() + ..() + del(src) + +// +// Crate Mimic +// + + +// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies. +/mob/living/simple_animal/hostile/vore/mimic/crate + + attacktext = "bitten" + + stop_automated_movement = 1 + wander = 0 + var/attempt_open = 0 + +// Pickup loot +/mob/living/simple_animal/hostile/vore/mimic/crate/New() + ..() + for(var/obj/item/I in loc) + I.loc = src + +/mob/living/simple_animal/hostile/vore/mimic/crate/DestroySurroundings() + ..() + if(prob(90)) + icon_state = "[initial(icon_state)]open" + else + icon_state = initial(icon_state) + +/mob/living/simple_animal/hostile/vore/mimic/crate/ListTargets() + if(attempt_open) + return ..() + return view(src, 1) + +/mob/living/simple_animal/hostile/vore/mimic/crate/FindTarget() + . = ..() + if(.) + trigger() + +/mob/living/simple_animal/hostile/vore/mimic/crate/AttackingTarget() + . = ..() + if(.) + icon_state = initial(icon_state) + +/mob/living/simple_animal/hostile/vore/mimic/crate/proc/trigger() + if(!attempt_open) + visible_message("[src] starts to move!") + attempt_open = 1 + +/mob/living/simple_animal/hostile/vore/mimic/crate/adjustBruteLoss(var/damage) + trigger() + ..(damage) + +/mob/living/simple_animal/hostile/vore/mimic/crate/LoseTarget() + ..() + icon_state = initial(icon_state) + +/mob/living/simple_animal/hostile/vore/mimic/crate/LostTarget() + ..() + icon_state = initial(icon_state) + +/mob/living/simple_animal/hostile/vore/mimic/crate/death() + + var/obj/structure/closet/crate/C = new(get_turf(src)) + // Put loot in crate + for(var/obj/O in src) + O.loc = C + ..() + +/mob/living/simple_animal/hostile/vore/mimic/crate/AttackingTarget() + . =..() + var/mob/living/L = . + if(istype(L)) + if(prob(15)) + L.Weaken(2) + L.visible_message("\the [src] knocks down \the [L]!") + +// +// Copy Mimic +// + +// var/global/list/protected_objects = list(/obj/structure/table, /obj/structure/cable, /obj/structure/window, /obj/item/projectile/animate) +// List now defined in projectile/animate.dm + +/mob/living/simple_animal/hostile/vore/mimic/copy + + health = 100 + maxHealth = 100 + var/mob/living/creator = null // the creator + var/destroy_objects = 0 + var/knockdown_people = 0 + +/mob/living/simple_animal/hostile/vore/mimic/copy/New(loc, var/obj/copy, var/mob/living/creator) + ..(loc) + CopyObject(copy, creator) + +/mob/living/simple_animal/hostile/vore/mimic/copy/death() + + for(var/atom/movable/M in src) + M.loc = get_turf(src) + ..() + +/mob/living/simple_animal/hostile/vore/mimic/copy/ListTargets() + // Return a list of targets that isn't the creator + . = ..() + return . - creator + +/mob/living/simple_animal/hostile/vore/mimic/copy/proc/CopyObject(var/obj/O, var/mob/living/creator) + + if((istype(O, /obj/item) || istype(O, /obj/structure)) && !is_type_in_list(O, protected_objects)) + + O.loc = src + name = O.name + desc = O.desc + icon = O.icon + icon_state = O.icon_state + icon_living = icon_state + + if(istype(O, /obj/structure)) + health = (anchored * 50) + 50 + destroy_objects = 1 + if(O.density && O.anchored) + knockdown_people = 1 + melee_damage_lower *= 2 + melee_damage_upper *= 2 + else if(istype(O, /obj/item)) + var/obj/item/I = O + health = 15 * I.w_class + melee_damage_lower = 2 + I.force + melee_damage_upper = 2 + I.force + move_to_delay = 2 * I.w_class + + maxHealth = health + if(creator) + src.creator = creator + faction = "\ref[creator]" // very unique + return 1 + return + +/mob/living/simple_animal/hostile/vore/mimic/copy/DestroySurroundings() + if(destroy_objects) + ..() + +/mob/living/simple_animal/hostile/vore/mimic/copy/AttackingTarget() + . =..() + if(knockdown_people) + var/mob/living/L = . + if(istype(L)) + if(prob(15)) + L.Weaken(1) + L.visible_message("\the [src] knocks down \the [L]!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/vore/vore.dm b/code/modules/mob/living/simple_animal/hostile/vore/vore.dm new file mode 100644 index 00000000000..650f621c604 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/vore/vore.dm @@ -0,0 +1,148 @@ +/* +-------------- +NOTES FOR DEVS +-------------- + +If your predator has a limited capacity, it should have sprites for every interval of its size, rounded to the nearest whole number. +Example: If I have a snake predator who has a capacity of 3, I need sprites for snake-1, snake-2, and snake-3. + +Capacity should always be a whole number. + +Also max_size and min_size should never exceed capacity or the icon will break. + +Don't use ranged mobs for vore mobs. +*/ + + +/mob/living/simple_animal/hostile/vore + name = "voracious lizard" + desc = "These gluttonous little bastards used to be regular lizards that were mutated by long-term exposure to phoron!" + icon_dead = "dino-dead" + icon_living = "dino" + icon_state = "dino" + icon = 'icons/mob/vore.dmi' + var/capacity = 1 // Zero is infinite. Do not set higher than you have icons to update. + var/max_size = 1 // Max: 2 + var/min_size = 0.25 // Min: 0.25 + var/picky = 1 // Won't eat undigestable prey by default + var/fullness = 0 + swallowTime = 10 // Hungry little bastards. + + // By default, this is what most vore mobs are capable of. + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "hits" + speed = 4 + harm_intent_damage = 5 + melee_damage_lower = 10 + melee_damage_upper = 25 + attacktext = "bitten" + attack_sound = 'sound/weapons/bite.ogg' + min_oxy = 5 + max_oxy = 0 + min_tox = 0 + max_tox = 1 + min_co2 = 0 + max_co2 = 5 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 270 + maxbodytemp = 370 + heat_damage_per_tick = 15 + cold_damage_per_tick = 10 + unsuitable_atoms_damage = 10 + +/mob/living/simple_animal/hostile/vore/update_icons() + if(stat == DEAD) + return + + if(capacity) + fullness = 0 + for(var/I in vore_organs) + var/datum/belly/B = vore_organs[I] + for(var/mob/living/M in B.internal_contents) + fullness += M.size_multiplier + fullness = round(fullness, 1) // Because intervals of 0.25 are going to make sprite artists cry. + if(fullness) + if (fullness > capacity) // Player controlled. + icon_state = "[initial(icon_state)]-[capacity]" + else + icon_state = "[initial(icon_state)]-[fullness]" + else + icon_state = initial(icon_state) + ..() + +/mob/living/simple_animal/hostile/vore/New() + + if(!vore_organs.len) + var/datum/belly/B = new /datum/belly(src) + B.immutable = 1 + B.name = "Stomach" + B.inside_flavor = "It appears to be rather warm and wet. Makes sense, considering it's inside \the [name]." + if (faction == "neutral") + B.digest_mode = "Hold" // Friendly slime-spawned mobs are neutral faction. + else + B.digest_mode = "Digest" // Though this usually doesn't happen. + vore_organs[B.name] = B + vore_selected = "Stomach" + B.vore_verb = "swallow" + B.emote_lists[DM_HOLD] = list( + "The insides knead at you gently for a moment.", + "The guts glorp wetly around you as some air shifts.", + "Your predator takes a deep breath and sighs, shifting you somewhat.", + "The stomach squeezes you tight for a moment, then relaxes.", + "During a moment of quiet, breathing becomes the most audible thing.", + "The warm slickness surrounds and kneads on you.") + + B.emote_lists[DM_DIGEST] = list( + "The caustic acids eat away at your form.", + "The acrid air burns at your lungs.", + "Without a thought for you, the stomach grinds inwards painfully.", + "The guts treat you like food, squeezing to press more acids against you.", + "The onslaught against your body doesn't seem to be letting up; you're food now.", + "The insides work on you like they would any other food.") + ..() + +/mob/living/simple_animal/hostile/vore/AttackingTarget() + if(isliving(target_mob.loc)) //They're inside a mob, maybe us, ignore! + return + + if(!isliving(target_mob)) //Can't eat 'em if they ain't alive. Prevents eating borgs/bots. + ..() + return + + if(picky && !target_mob.digestable) //Don't eat people with nogurgle prefs + ..() + return + + if(target_mob.lying && target_mob.size_multiplier >= min_size && target_mob.size_multiplier <= max_size && !(target_mob in prey_exclusions)) + if(capacity) + var/check_size = target_mob.size_multiplier + fullness + if(check_size <= capacity) + animal_nom(target_mob) + target_mob = null + else + animal_nom(target_mob) + target_mob = null + + ..() + +/mob/living/simple_animal/hostile/vore/death() + for(var/I in vore_organs) + var/datum/belly/B = vore_organs[I] + B.release_all_contents() // When your stomach is empty + ..() // then you have my permission to die. + + +// ------- Big Preds ------- // + +/mob/living/simple_animal/hostile/vore/large + name = "giant snake" + desc = "Snakes. Why did it have to be snakes?" + icon = 'icons/mob/vore64x64.dmi' + icon_dead = "snake-dead" + icon_living = "snake" + icon_state = "snake" + pixel_x = -16 + maxHealth = 200 + health = 200 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/simple_animal_vr.dm b/code/modules/mob/living/simple_animal/simple_animal_vr.dm new file mode 100644 index 00000000000..ec0d15bcd7e --- /dev/null +++ b/code/modules/mob/living/simple_animal/simple_animal_vr.dm @@ -0,0 +1,3 @@ +/mob/living/simple_animal + // List of targets excluded (for now) from being eaten by this mob. + var/list/prey_exclusions = list() diff --git a/icons/mob/fox_vr.dmi b/icons/mob/fox_vr.dmi new file mode 100644 index 00000000000..79d1671408e Binary files /dev/null and b/icons/mob/fox_vr.dmi differ diff --git a/icons/mob/snake_vr.dmi b/icons/mob/snake_vr.dmi new file mode 100644 index 00000000000..42631b85de9 Binary files /dev/null and b/icons/mob/snake_vr.dmi differ diff --git a/icons/mob/vore.dmi b/icons/mob/vore.dmi new file mode 100644 index 00000000000..fd2050b7a30 Binary files /dev/null and b/icons/mob/vore.dmi differ diff --git a/icons/mob/vore64x64.dmi b/icons/mob/vore64x64.dmi new file mode 100644 index 00000000000..550817b8394 Binary files /dev/null and b/icons/mob/vore64x64.dmi differ diff --git a/vorestation.dme b/vorestation.dme index 11f067efc3b..658a8f5362c 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -1499,6 +1499,7 @@ #include "code\modules\mob\living\simple_animal\parrot.dm" #include "code\modules\mob\living\simple_animal\shade.dm" #include "code\modules\mob\living\simple_animal\simple_animal.dm" +#include "code\modules\mob\living\simple_animal\simple_animal_vr.dm" #include "code\modules\mob\living\simple_animal\worm.dm" #include "code\modules\mob\living\simple_animal\borer\borer.dm" #include "code\modules\mob\living\simple_animal\borer\borer_captive.dm" @@ -1511,10 +1512,12 @@ #include "code\modules\mob\living\simple_animal\friendly\corgi_vr.dm" #include "code\modules\mob\living\simple_animal\friendly\crab.dm" #include "code\modules\mob\living\simple_animal\friendly\farm_animals.dm" +#include "code\modules\mob\living\simple_animal\friendly\fox_vr.dm" #include "code\modules\mob\living\simple_animal\friendly\lizard.dm" #include "code\modules\mob\living\simple_animal\friendly\mouse.dm" #include "code\modules\mob\living\simple_animal\friendly\mushroom.dm" #include "code\modules\mob\living\simple_animal\friendly\slime.dm" +#include "code\modules\mob\living\simple_animal\friendly\snake_vr.dm" #include "code\modules\mob\living\simple_animal\friendly\spiderbot.dm" #include "code\modules\mob\living\simple_animal\friendly\tomato.dm" #include "code\modules\mob\living\simple_animal\hostile\alien.dm" @@ -1534,6 +1537,12 @@ #include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm" +#include "code\modules\mob\living\simple_animal\hostile\vore\alien.dm" +#include "code\modules\mob\living\simple_animal\hostile\vore\bear.dm" +#include "code\modules\mob\living\simple_animal\hostile\vore\carp.dm" +#include "code\modules\mob\living\simple_animal\hostile\vore\creature.dm" +#include "code\modules\mob\living\simple_animal\hostile\vore\mimic.dm" +#include "code\modules\mob\living\simple_animal\hostile\vore\vore.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"