Merge pull request #750 from Shadowfire117/plantmobs

changes to space vines, additional ranged mob behaviour and three plant mobs
This commit is contained in:
Razgriz
2019-08-12 02:04:03 -07:00
committed by GitHub
6 changed files with 1806 additions and 1771 deletions
@@ -1,166 +1,175 @@
#define NEIGHBOR_REFRESH_TIME 100
/obj/effect/plant/proc/get_cardinal_neighbors()
var/list/cardinal_neighbors = list()
for(var/check_dir in cardinal)
var/turf/simulated/T = get_step(get_turf(src), check_dir)
if(istype(T))
cardinal_neighbors |= T
return cardinal_neighbors
/obj/effect/plant/proc/update_neighbors()
// Update our list of valid neighboring turfs.
neighbors = list()
for(var/turf/simulated/floor in get_cardinal_neighbors())
if(get_dist(parent, floor) > spread_distance)
continue
var/blocked = 0
for(var/obj/effect/plant/other in floor.contents)
if(other.seed == src.seed)
blocked = 1
break
if(blocked)
continue
if(floor.density)
if(!isnull(seed.chems["pacid"]))
spawn(rand(5,25)) floor.ex_act(3)
continue
if(!Adjacent(floor) || !floor.Enter(src))
continue
neighbors |= floor
if(neighbors.len)
plant_controller.add_plant(src) //if we have neighbours again, start processing
// Update all of our friends.
var/turf/T = get_turf(src)
for(var/obj/effect/plant/neighbor in range(1,src))
if(neighbor.seed == src.seed)
neighbor.neighbors -= T
/obj/effect/plant/process()
// Something is very wrong, kill ourselves.
if(!seed)
die_off()
return 0
for(var/obj/effect/effect/smoke/chem/smoke in view(1, src))
if(smoke.reagents.has_reagent("plantbgone"))
die_off()
return
// Handle life.
var/turf/simulated/T = get_turf(src)
if(istype(T))
health -= seed.handle_environment(T,T.return_air(),null,1)
if(health < max_health)
health += rand(3,5)
refresh_icon()
if(health > max_health)
health = max_health
else if(health == max_health && !plant)
plant = new(T,seed)
plant.dir = src.dir
plant.transform = src.transform
plant.age = seed.get_trait(TRAIT_MATURATION)-1
plant.update_icon()
if(growth_type==0) //Vines do not become invisible.
invisibility = INVISIBILITY_MAXIMUM
else
plant.layer = layer + 0.1
if(has_buckled_mobs())
for(var/A in buckled_mobs)
var/mob/living/L = A
seed.do_sting(L,src)
if(seed.get_trait(TRAIT_CARNIVOROUS))
seed.do_thorns(L,src)
if(world.time >= last_tick+NEIGHBOR_REFRESH_TIME)
last_tick = world.time
update_neighbors()
if(sampled)
//Should be between 2-7 for given the default range of values for TRAIT_PRODUCTION
var/chance = max(1, round(15/seed.get_trait(TRAIT_PRODUCTION)))
if(prob(chance))
sampled = 0
if(is_mature() && !has_buckled_mobs())
for(var/turf/neighbor in neighbors)
for(var/mob/living/M in neighbor)
if(seed.get_trait(TRAIT_SPREAD) >= 2 && (M.lying || prob(round(seed.get_trait(TRAIT_POTENCY)))))
entangle(M)
if(is_mature() && neighbors.len && prob(spread_chance))
//spread to 1-3 adjacent turfs depending on yield trait.
var/max_spread = between(1, round(seed.get_trait(TRAIT_YIELD)*3/14), 3)
for(var/i in 1 to max_spread)
if(prob(spread_chance))
sleep(rand(3,5))
if(!neighbors.len)
break
spread_to(pick(neighbors))
// We shouldn't have spawned if the controller doesn't exist.
check_health()
if(has_buckled_mobs() || neighbors.len)
plant_controller.add_plant(src)
//spreading vines aren't created on their final turf.
//Instead, they are created at their parent and then move to their destination.
/obj/effect/plant/proc/spread_to(turf/target_turf)
var/obj/effect/plant/child = new(get_turf(src),seed,parent)
spawn(1) // This should do a little bit of animation.
if(QDELETED(child))
return
//move out to the destination
child.anchored = 0
step_to(child, target_turf)
child.anchored = 1
child.update_icon()
//see if anything is there
for(var/thing in child.loc)
if(thing != child && istype(thing, /obj/effect/plant))
var/obj/effect/plant/other = thing
if(other.seed != child.seed)
other.vine_overrun(child.seed, src) //vine fight
qdel(child)
return
if(istype(thing, /obj/effect/dead_plant))
qdel(thing)
qdel(child)
return
if(isliving(thing) && (seed.get_trait(TRAIT_CARNIVOROUS) || (seed.get_trait(TRAIT_SPREAD) >= 2 && prob(round(seed.get_trait(TRAIT_POTENCY))))))
entangle(thing)
qdel(child)
return
// Update neighboring squares.
for(var/obj/effect/plant/neighbor in range(1, child.loc)) //can use the actual final child loc now
if(child.seed == neighbor.seed) //neighbors of different seeds will continue to try to overrun each other
neighbor.neighbors -= target_turf
child.finish_spreading()
/obj/effect/plant/proc/die_off()
// Kill off our plant.
if(plant) plant.die()
// This turf is clear now, let our buddies know.
for(var/turf/simulated/check_turf in get_cardinal_neighbors())
if(!istype(check_turf))
continue
for(var/obj/effect/plant/neighbor in check_turf.contents)
neighbor.neighbors |= check_turf
plant_controller.add_plant(neighbor)
spawn(1) if(src) qdel(src)
#define NEIGHBOR_REFRESH_TIME 100
/obj/effect/plant/proc/get_cardinal_neighbors()
var/list/cardinal_neighbors = list()
for(var/check_dir in cardinal)
var/turf/simulated/T = get_step(get_turf(src), check_dir)
if(istype(T))
cardinal_neighbors |= T
return cardinal_neighbors
/obj/effect/plant/proc/update_neighbors()
// Update our list of valid neighboring turfs.
neighbors = list()
for(var/turf/simulated/floor in get_cardinal_neighbors())
if(get_dist(parent, floor) > spread_distance)
continue
var/blocked = 0
for(var/obj/effect/plant/other in floor.contents)
if(other.seed == src.seed)
blocked = 1
break
if(blocked)
continue
if(floor.density)
if(!isnull(seed.chems["pacid"]))
spawn(rand(5,25)) floor.ex_act(3)
continue
if(!Adjacent(floor) || !floor.Enter(src))
continue
neighbors |= floor
if(neighbors.len)
plant_controller.add_plant(src) //if we have neighbours again, start processing
// Update all of our friends.
var/turf/T = get_turf(src)
for(var/obj/effect/plant/neighbor in range(1,src))
if(neighbor.seed == src.seed)
neighbor.neighbors -= T
/obj/effect/plant/process()
// Something is very wrong, kill ourselves.
if(!seed)
die_off()
return 0
for(var/obj/effect/effect/smoke/chem/smoke in view(1, src))
if(smoke.reagents.has_reagent("plantbgone"))
die_off()
return
// Handle life.
var/turf/simulated/T = get_turf(src)
if(istype(T))
health -= seed.handle_environment(T,T.return_air(),null,1)
if(health < max_health)
health += rand(3,5)
refresh_icon()
if(health > max_health)
health = max_health
else if(health == max_health && !plant)
plant = new(T,seed)
plant.dir = src.dir
plant.transform = src.transform
plant.age = seed.get_trait(TRAIT_MATURATION)-1
plant.update_icon()
if(growth_type==0) //Vines do not become invisible.
invisibility = INVISIBILITY_MAXIMUM
else
plant.layer = layer + 0.1
if(has_buckled_mobs())
for(var/A in buckled_mobs)
var/mob/living/L = A
seed.do_sting(L,src)
if(seed.get_trait(TRAIT_CARNIVOROUS))
seed.do_thorns(L,src)
if(world.time >= last_tick+NEIGHBOR_REFRESH_TIME)
last_tick = world.time
update_neighbors()
if(sampled)
//Should be between 2-7 for given the default range of values for TRAIT_PRODUCTION
var/chance = max(1, round(15/seed.get_trait(TRAIT_PRODUCTION)))
if(prob(chance))
sampled = 0
if(is_mature() && !has_buckled_mobs())
for(var/turf/neighbor in neighbors)
for(var/mob/living/M in neighbor)
if(seed.get_trait(TRAIT_SPREAD) >= 2 && (M.lying || prob(round(seed.get_trait(TRAIT_POTENCY)))))
entangle(M)
if(is_mature() && neighbors.len && prob(spread_chance))
//spread to 1-3 adjacent turfs depending on yield trait.
var/max_spread = between(1, round(seed.get_trait(TRAIT_YIELD)*3/14), 3)
for(var/i in 1 to max_spread)
if(prob(spread_chance))
sleep(rand(3,5))
if(!neighbors.len)
break
spread_to(pick(neighbors))
// We shouldn't have spawned if the controller doesn't exist.
check_health()
if(has_buckled_mobs() || neighbors.len)
plant_controller.add_plant(src)
//spreading vines aren't created on their final turf.
//Instead, they are created at their parent and then move to their destination.
/obj/effect/plant/proc/spread_to(turf/target_turf)
var/obj/effect/plant/child = new(get_turf(src),seed,parent)
spawn(1) // This should do a little bit of animation.
if(QDELETED(child))
return
//move out to the destination
child.anchored = 0
step_to(child, target_turf)
child.anchored = 1
child.update_icon()
if((seed.get_trait(TRAIT_POTENCY)) >= 50 && (seed.get_trait(TRAIT_CARNIVOROUS)) && prob(5))
var/spread_mobspawn = rand(0,2)
switch(spread_mobspawn)
if(0) new /mob/living/simple_animal/hostile/piranhaplant(src.loc)
if(1) new /mob/living/simple_animal/hostile/piranhaplant/spitter(src.loc)
if(2) new /mob/living/simple_animal/hostile/piranhaplant/pitcher(src.loc)
//see if anything is there
for(var/thing in child.loc)
if(thing != child && istype(thing, /obj/effect/plant))
var/obj/effect/plant/other = thing
if(other.seed != child.seed)
other.vine_overrun(child.seed, src) //vine fight
qdel(child)
return
if(istype(thing, /obj/effect/dead_plant))
qdel(thing)
qdel(child)
return
if(isliving(thing) && (seed.get_trait(TRAIT_CARNIVOROUS) || (seed.get_trait(TRAIT_SPREAD) >= 2 && prob(round(seed.get_trait(TRAIT_POTENCY))))))
entangle(thing)
qdel(child)
return
// Update neighboring squares.
for(var/obj/effect/plant/neighbor in range(1, child.loc)) //can use the actual final child loc now
if(child.seed == neighbor.seed) //neighbors of different seeds will continue to try to overrun each other
neighbor.neighbors -= target_turf
child.finish_spreading()
/obj/effect/plant/proc/die_off()
// Kill off our plant.
if(plant) plant.die()
// This turf is clear now, let our buddies know.
for(var/turf/simulated/check_turf in get_cardinal_neighbors())
if(!istype(check_turf))
continue
for(var/obj/effect/plant/neighbor in check_turf.contents)
neighbor.neighbors |= check_turf
plant_controller.add_plant(neighbor)
spawn(1) if(src) qdel(src)
#undef NEIGHBOR_REFRESH_TIME
@@ -1,100 +1,106 @@
/obj/effect/plant/HasProximity(var/atom/movable/AM)
if(!is_mature() || seed.get_trait(TRAIT_SPREAD) != 2)
return
var/mob/living/M = AM
if(!istype(M))
return
if(!has_buckled_mobs() && !M.buckled && !M.anchored && (issmall(M) || prob(round(seed.get_trait(TRAIT_POTENCY)/3))))
//wait a tick for the Entered() proc that called HasProximity() to finish (and thus the moving animation),
//so we don't appear to teleport from two tiles away when moving into a turf adjacent to vines.
spawn(1)
entangle(M)
/obj/effect/plant/attack_hand(var/mob/user)
manual_unbuckle(user)
/obj/effect/plant/attack_generic(var/mob/user)
manual_unbuckle(user)
/obj/effect/plant/Crossed(atom/movable/O)
if(isliving(O))
trodden_on(O)
/obj/effect/plant/proc/trodden_on(var/mob/living/victim)
if(!is_mature())
return
var/mob/living/carbon/human/H = victim
if(prob(round(seed.get_trait(TRAIT_POTENCY)/3)))
entangle(victim)
if(istype(H) && H.shoes)
return
seed.do_thorns(victim,src)
seed.do_sting(victim,src,pick("r_foot","l_foot","r_leg","l_leg"))
/obj/effect/plant/proc/unbuckle()
if(has_buckled_mobs())
for(var/A in buckled_mobs)
var/mob/living/L = A
if(L.buckled == src)
L.buckled = null
L.anchored = initial(L.anchored)
L.update_canmove()
buckled_mobs = list()
return
/obj/effect/plant/proc/manual_unbuckle(mob/user as mob)
if(has_buckled_mobs())
var/chance = 20
if(seed)
chance = round(100/(20*seed.get_trait(TRAIT_POTENCY)/100))
if(prob(chance))
for(var/A in buckled_mobs)
var/mob/living/L = A
if(!(user in buckled_mobs))
L.visible_message(\
"<span class='notice'>\The [user] frees \the [L] from \the [src].</span>",\
"<span class='notice'>\The [user] frees you from \the [src].</span>",\
"<span class='warning'>You hear shredding and ripping.</span>")
else
L.visible_message(\
"<span class='notice'>\The [L] struggles free of \the [src].</span>",\
"<span class='notice'>You untangle \the [src] from around yourself.</span>",\
"<span class='warning'>You hear shredding and ripping.</span>")
unbuckle()
else
user.setClickCooldown(user.get_attack_speed())
health -= rand(1,5)
var/text = pick("rip","tear","pull", "bite", "tug")
user.visible_message(\
"<span class='warning'>\The [user] [text]s at \the [src].</span>",\
"<span class='warning'>You [text] at \the [src].</span>",\
"<span class='warning'>You hear shredding and ripping.</span>")
check_health()
return
/obj/effect/plant/proc/entangle(var/mob/living/victim)
if(has_buckled_mobs())
return
if(victim.buckled || victim.anchored)
return
//grabbing people
if(!victim.anchored && Adjacent(victim) && victim.loc != src.loc)
var/can_grab = 1
if(istype(victim, /mob/living/carbon/human))
var/mob/living/carbon/human/H = victim
if(istype(H.shoes, /obj/item/clothing/shoes/magboots) && (H.shoes.item_flags & NOSLIP))
can_grab = 0
if(can_grab)
src.visible_message("<span class='danger'>Tendrils lash out from \the [src] and drag \the [victim] in!</span>")
victim.forceMove(src.loc)
buckle_mob(victim)
victim.set_dir(pick(cardinal))
victim << "<span class='danger'>Tendrils [pick("wind", "tangle", "tighten")] around you!</span>"
victim.Weaken(0.5)
seed.do_thorns(victim,src)
/obj/effect/plant/HasProximity(var/atom/movable/AM)
if(!is_mature() || seed.get_trait(TRAIT_SPREAD) != 2)
return
var/mob/living/M = AM
if(!istype(M))
return
if(!has_buckled_mobs() && !M.buckled && !M.anchored && (issmall(M) || prob(round(seed.get_trait(TRAIT_POTENCY)/3))))
//wait a tick for the Entered() proc that called HasProximity() to finish (and thus the moving animation),
//so we don't appear to teleport from two tiles away when moving into a turf adjacent to vines.
if(M.entangle_immunity == 1)
return
spawn(1)
entangle(M)
/obj/effect/plant/attack_hand(var/mob/user)
manual_unbuckle(user)
/obj/effect/plant/attack_generic(var/mob/user)
manual_unbuckle(user)
/obj/effect/plant/Crossed(atom/movable/O)
if(isliving(O))
trodden_on(O)
/obj/effect/plant/proc/trodden_on(var/mob/living/victim)
if(!is_mature())
return
var/mob/living/carbon/human/H = victim
if(H.entangle_immunity == 1)
return
if(prob(round(seed.get_trait(TRAIT_POTENCY)/3)))
entangle(victim)
if(istype(H) && H.shoes)
return
seed.do_thorns(victim,src)
seed.do_sting(victim,src,pick("r_foot","l_foot","r_leg","l_leg"))
/obj/effect/plant/proc/unbuckle()
if(has_buckled_mobs())
for(var/A in buckled_mobs)
var/mob/living/L = A
if(L.buckled == src)
L.buckled = null
L.anchored = initial(L.anchored)
L.update_canmove()
buckled_mobs = list()
return
/obj/effect/plant/proc/manual_unbuckle(mob/user as mob)
if(has_buckled_mobs())
var/chance = 20
if(seed)
chance = round(100/(20*seed.get_trait(TRAIT_POTENCY)/100))
if(prob(chance))
for(var/A in buckled_mobs)
var/mob/living/L = A
if(!(user in buckled_mobs))
L.visible_message(\
"<span class='notice'>\The [user] frees \the [L] from \the [src].</span>",\
"<span class='notice'>\The [user] frees you from \the [src].</span>",\
"<span class='warning'>You hear shredding and ripping.</span>")
else
L.visible_message(\
"<span class='notice'>\The [L] struggles free of \the [src].</span>",\
"<span class='notice'>You untangle \the [src] from around yourself.</span>",\
"<span class='warning'>You hear shredding and ripping.</span>")
unbuckle()
else
user.setClickCooldown(user.get_attack_speed())
health -= rand(1,5)
var/text = pick("rip","tear","pull", "bite", "tug")
user.visible_message(\
"<span class='warning'>\The [user] [text]s at \the [src].</span>",\
"<span class='warning'>You [text] at \the [src].</span>",\
"<span class='warning'>You hear shredding and ripping.</span>")
check_health()
return
/obj/effect/plant/proc/entangle(var/mob/living/victim)
if(has_buckled_mobs())
return
if(victim.buckled || victim.anchored)
return
//grabbing people
if(!victim.anchored && Adjacent(victim) && victim.loc != src.loc)
var/can_grab = 1
if(istype(victim, /mob/living/carbon/human))
var/mob/living/carbon/human/H = victim
if(H.entangle_immunity == 1)
return
if(istype(H.shoes, /obj/item/clothing/shoes/magboots) && (H.shoes.item_flags & NOSLIP))
can_grab = 0
if(can_grab)
src.visible_message("<span class='danger'>Tendrils lash out from \the [src] and drag \the [victim] in!</span>")
victim.forceMove(src.loc)
buckle_mob(victim)
victim.set_dir(pick(cardinal))
victim << "<span class='danger'>Tendrils [pick("wind", "tangle", "tighten")] around you!</span>"
victim.Weaken(0.5)
seed.do_thorns(victim,src)
File diff suppressed because it is too large Load Diff
@@ -1,215 +1,226 @@
/mob/living/simple_animal/hostile/tomato
name = "tomato"
desc = "It's a horrifyingly enormous beef tomato, and it's packing extra beef!"
tt_desc = "X Solanum abominable"
icon_state = "tomato"
icon_living = "tomato"
icon_dead = "tomato_dead"
intelligence_level = SA_PLANT
faction = "plants"
maxHealth = 15
health = 15
turns_per_move = 5
response_help = "prods"
response_disarm = "pushes aside"
response_harm = "smacks"
harm_intent_damage = 5
melee_damage_upper = 15
melee_damage_lower = 10
attacktext = list("mauled")
meat_type = /obj/item/weapon/reagent_containers/food/snacks/tomatomeat
//CHOMPEDIT PIRANHA PLANT.
//When I stop being lazy I'll give this its own file -shark
//Yes I'm basing this shit on the tomato, sue me. -shark
//No longer based on tomato because evolved too far -shark
/mob/living/simple_animal/hostile/piranhaplant
name = "Piranha Plant"
desc = "It's a plant, that eats people!"
tt_desc = "Packun Flower"
faction = "plants"
intelligence_level = SA_PLANT
maxHealth = 50
health = 50
meat_type = null
//Mob icon/appearance settings
icon = 'icons/mob/plantmobs32x32.dmi' //Thanks to vorebound mod and Estelle
icon_living = "piranha-plant"
icon_state = "piranha-plant"
icon_dead = "piranha-plant_dead"
icon_gib = "generic_gib" // The iconstate for being gibbed, optional. Defaults to a generic gib animation.
//icon_rest = null // The iconstate for resting, optional
attack_icon = 'icons/effects/effects.dmi' //Just the default, played like the weapon attack anim
attack_icon_state = "slash" //Just the default //gonna have to make teeth chomping version
//Vore stuff
vore_active = 1
vore_capacity = 1
vore_pounce_chance = 10
vore_standing_too = 1
vore_ignores_undigestable = 0
vore_default_mode = DM_DIGEST
vore_digest_chance = 99
vore_absorb_chance = 0
vore_escape_chance = 5
vore_icons = SA_ICON_LIVING
swallowTime = 10 SECONDS //CHOMPED
//Movement Stuff
wander = 0 // Does the mob wander around when idle?
wander_distance = 0 // How far the mob will wander before going home (assuming they are allowed to do that)
returns_home = 1 // Mob knows how to return to wherever it started
turns_per_move = 4 // How many life() cycles to wait between each wander mov?
stop_when_pulled = 0 // When set to 1 this stops the animal from moving when someone is pulling it.
follow_dist = 0 // Distance the mob tries to follow a friend
speed = 4 // Higher speed is slower, negative speed is faster.
//Talk/Emote stuff
speak_chance = 0 // Probability that I talk (this is 'X in 200' chance since even 1/100 is pretty noisy)
reacts = 1 // Reacts to some things being said
speak = list() // Things I might say if I talk
emote_hear = list("chomps","snaps at the air") // Hearable emotes I might perform
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
say_understood = list() // List of things to say when accepting an order
say_cannot = list() // List of things to say when they cannot comply
say_maybe_target = list() // List of things to say when they spot something barely
say_got_target = list() // List of things to say when they engage a target
reactions = list("chomp" = "!chomps",) // List of "string" = "reaction" and things they hear will be searched for string.
//Hostility war bloodshed, RAWR
hostile = 1 // Do I even attack?
view_range = 2 // Scan for targets in this range.
investigates = 1 // Do I investigate if I saw someone briefly?
cooperative = 1 // Do I ask allies to help me?
assist_distance = 2 // Radius in which I'll ask my comrades for help.
grab_resist = 100 // Chance of me resisting a grab attempt.
taser_kill = 1 // Is the mob weak to tasers
//Melee behaviour
melee_damage_lower = 1 // Lower bound of randomized melee damage
melee_damage_upper = 25 // Upper bound of randomized melee damage
attacktext = list("chomped","bit","hompfed","crunched","cronched") // "You are [attacktext] by the mob!"
friendly = list("nuzzles") // "The mob [friendly] the person."
//attack_sound = null // Sound to play when I attack
environment_smash = 0 // How much environment damage do I do when I hit stuff?
melee_miss_chance = 1 // percent chance to miss a melee attack.
melee_attack_minDelay = 5 // How long between attacks at least
melee_attack_maxDelay = 20 // How long between attacks at most
attack_armor_type = "bio" // What armor does this check?
attack_armor_pen = 50 // How much armor pen this attack has.
attack_sharp = 1 // Is the attack sharp?
attack_edge = 0 // Does the attack have an edge?
//Stuff for people wanting to be a fucking plant. Weirdos
show_stat_health = 1 // Does the percentage health show in the stat panel for the mob
ai_inactive = 0 // Set to 1 to turn off most AI actions
has_hands = 1 // Set to 1 to enable the use of hands and the hands hud
humanoid_hands = 1 // Can a player in this mob use things like guns or AI cards?
//hand_form = "hands" // Used in IsHumanoidToolUser. 'Your X are not fit-'.
//hud_gears // Slots to show on the hud (typically none)
//ui_icons // Icon file path to use for the HUD, otherwise generic icons are used
//r_hand_sprite = "piranha_r" // If they have hands, //TODO make a leaf sprite for this
//l_hand_sprite = "piranha_l" // they could use some icons.
player_msg = "PLANT GO CHOMP" // Message to print to players about 'how' to play this mob on login.
//Ranged variation
/mob/living/simple_animal/hostile/piranhaplant/spitter
//might snatch the code for that uranium ray for this since it should poison
name = "Piranha Spitter"
attack_armor_pen = 0
//Attack ranged settings.
ranged = 1 // Do I attack at range?
shoot_range = 6 // How far away do I start shooting from?
view_range = 5 //More range, more hurt, more... plant?
rapid = 1 // Three-round-burst fire mode
firing_lines = 0 // Avoids shooting allies
projectiletype = /obj/item/projectile/energy/piranhaspit // The projectiles I shoot
projectilesound = 'sound/weapons/thudswoosh.ogg' // The sound I make when I do it
casingtype = /obj/item/weapon/reagent_containers/food/snacks/soylentgreen/piranha // What to make the hugely laggy casings pile out of
//Piranha unique projectile
/obj/item/projectile/energy/piranhaspit
name = "piranhaspit"
icon_state = "neurotoxin"
damage = 4 //Reduced damage to 4 from 10, 3 fired projectiles mean might do 12 total if all 3 hit
damage_type = TOX
check_armour = "bio" //yup biohazard protection works here
flash_strength = 0
agony = 10
combustion = FALSE
/obj/item/weapon/reagent_containers/food/snacks/soylentgreen/piranha
name = "Soylent"
desc = "This was spat out by a strange plant that eats people."
icon_state = "soylent_green"
filling_color = "#B8E6B5"
center_of_mass = list("x"=15, "y"=11)
/obj/item/projectile/energy/piranhaspit/on_hit(var/atom/soyled)
soyl(soyled)
..()
/obj/item/projectile/energy/piranhaspit/proc/soyl(var/mob/M)
var/location = get_turf(M)
new /obj/item/weapon/reagent_containers/food/snacks/soylentgreen/piranha(location)
//VORE FLUFF section and extended gut settings
/mob/living/simple_animal/hostile/piranhaplant/init_vore()
..()
var/obj/belly/B = vore_selected
B.vore_verb = "chomp up"
B.name = "stomach"
B.desc = "You're pulled into the tight stomach of the plant. The walls knead weakly around you, coating you in thick, viscous fluids that cling to your body, that soon starts to tingle and burn..."
B.digest_burn = 0
B.digest_brute = 12
/mob/living/simple_animal/hostile/piranhaplant/pitcher
icon_state = "pitcher"
icon_living = "pitcher"
name = "Pitcher Plant"
desc = "It's a plant! How pretty"
tt_desc = "Brig Flower"
health = 50
maxHealth = 50 //starts with 50
var/antispam = 0
/mob/living/simple_animal/hostile/piranhaplant/pitcher/death()
..()
new /obj/item/weapon/reagent_containers/food/snacks/aesirsalad(location)
new /obj/item/weapon/reagent_containers/food/snacks/aesirsalad(location)
new /obj/item/weapon/reagent_containers/food/snacks/aesirsalad(location)
new /obj/item/weapon/reagent_containers/food/snacks/aesirsalad(location)
qdel(src)
/mob/living/simple_animal/hostile/piranhaplant/pitcher/Life()
..()
if(!anchored)
anchored=1
if(maxHealth <= 499) //Ok maybe there are limits
maxHealth = health //Limits are merely a suggestion
if(vore_fullness && !antispam)
antispam = 1
spawn(10)
if(maxHealth <= 499)
maxHealth += 1
health += 1
antispam = !antispam
if(size_multiplier!=1*health/100)
size_multiplier=1*health/100
update_icons()
/mob/living/simple_animal/hostile/piranhaplant/pitcher/init_vore()
..()
var/obj/belly/B = vore_selected
B.digest_burn = 0.5
B.digest_brute = 0
B.vore_verb = "slurped up"
B.name = "pitcher"
/mob/living/simple_animal/hostile/tomato
name = "tomato"
desc = "It's a horrifyingly enormous beef tomato, and it's packing extra beef!"
tt_desc = "X Solanum abominable"
icon_state = "tomato"
icon_living = "tomato"
icon_dead = "tomato_dead"
intelligence_level = SA_PLANT
faction = "plants"
maxHealth = 15
health = 15
turns_per_move = 5
response_help = "prods"
response_disarm = "pushes aside"
response_harm = "smacks"
harm_intent_damage = 5
melee_damage_upper = 15
melee_damage_lower = 10
attacktext = list("mauled")
meat_type = /obj/item/weapon/reagent_containers/food/snacks/tomatomeat
//CHOMPEDIT PIRANHA PLANT.
//When I stop being lazy I'll give this its own file -shark
//Yes I'm basing this shit on the tomato, sue me. -shark
//No longer based on tomato because evolved too far -shark
/mob/living/simple_animal/hostile/piranhaplant
name = "Piranha Plant"
desc = "It's a plant, that eats people!"
tt_desc = "Packun Flower"
faction = "plants"
intelligence_level = SA_PLANT
maxHealth = 50
health = 50
meat_type = null
//Mob icon/appearance settings
icon = 'icons/mob/plantmobs32x32.dmi' //Thanks to vorebound mod and Estelle
icon_living = "piranha-plant"
icon_state = "piranha-plant"
icon_dead = "piranha-plant_dead"
icon_gib = "generic_gib" // The iconstate for being gibbed, optional. Defaults to a generic gib animation.
//icon_rest = null // The iconstate for resting, optional
attack_icon = 'icons/effects/effects.dmi' //Just the default, played like the weapon attack anim
attack_icon_state = "slash" //Just the default //gonna have to make teeth chomping version
//Vore stuff
vore_active = 1
vore_capacity = 1
vore_pounce_chance = 10
vore_standing_too = 1
vore_ignores_undigestable = 0
vore_default_mode = DM_DIGEST
vore_digest_chance = 99
vore_absorb_chance = 0
vore_escape_chance = 5
vore_icons = SA_ICON_LIVING
swallowTime = 10 SECONDS //CHOMPED
//Movement Stuff
wander = 0 // Does the mob wander around when idle?
wander_distance = 0 // How far the mob will wander before going home (assuming they are allowed to do that)
returns_home = 1 // Mob knows how to return to wherever it started
turns_per_move = 4 // How many life() cycles to wait between each wander mov?
stop_when_pulled = 0 // When set to 1 this stops the animal from moving when someone is pulling it.
follow_dist = 0 // Distance the mob tries to follow a friend
speed = 4 // Higher speed is slower, negative speed is faster.
entangle_immunity = 1 //makes mob immune to entangle effect of vines and also wont get stabbed by vines that has thorns
//Talk/Emote stuff
speak_chance = 0 // Probability that I talk (this is 'X in 200' chance since even 1/100 is pretty noisy)
reacts = 1 // Reacts to some things being said
speak = list() // Things I might say if I talk
emote_hear = list("chomps","snaps at the air") // Hearable emotes I might perform
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
say_understood = list() // List of things to say when accepting an order
say_cannot = list() // List of things to say when they cannot comply
say_maybe_target = list() // List of things to say when they spot something barely
say_got_target = list() // List of things to say when they engage a target
reactions = list("chomp" = "!chomps",) // List of "string" = "reaction" and things they hear will be searched for string.
//Hostility war bloodshed, RAWR
hostile = 1 // Do I even attack?
view_range = 2 // Scan for targets in this range.
investigates = 1 // Do I investigate if I saw someone briefly?
cooperative = 1 // Do I ask allies to help me?
assist_distance = 2 // Radius in which I'll ask my comrades for help.
grab_resist = 100 // Chance of me resisting a grab attempt.
taser_kill = 1 // Is the mob weak to tasers
//Melee behaviour
melee_damage_lower = 1 // Lower bound of randomized melee damage
melee_damage_upper = 25 // Upper bound of randomized melee damage
attacktext = list("chomped","bit","hompfed","crunched","cronched") // "You are [attacktext] by the mob!"
friendly = list("nuzzles") // "The mob [friendly] the person."
//attack_sound = null // Sound to play when I attack
environment_smash = 0 // How much environment damage do I do when I hit stuff?
melee_miss_chance = 1 // percent chance to miss a melee attack.
melee_attack_minDelay = 5 // How long between attacks at least
melee_attack_maxDelay = 20 // How long between attacks at most
attack_armor_type = "bio" // What armor does this check?
attack_armor_pen = 50 // How much armor pen this attack has.
attack_sharp = 1 // Is the attack sharp?
attack_edge = 0 // Does the attack have an edge?
//Stuff for people wanting to be a fucking plant. Weirdos
show_stat_health = 1 // Does the percentage health show in the stat panel for the mob
ai_inactive = 0 // Set to 1 to turn off most AI actions
has_hands = 1 // Set to 1 to enable the use of hands and the hands hud
humanoid_hands = 1 // Can a player in this mob use things like guns or AI cards?
//hand_form = "hands" // Used in IsHumanoidToolUser. 'Your X are not fit-'.
//hud_gears // Slots to show on the hud (typically none)
//ui_icons // Icon file path to use for the HUD, otherwise generic icons are used
//r_hand_sprite = "piranha_r" // If they have hands, //TODO make a leaf sprite for this
//l_hand_sprite = "piranha_l" // they could use some icons.
player_msg = "PLANT GO CHOMP" // Message to print to players about 'how' to play this mob on login.
//Ranged variation
/mob/living/simple_animal/hostile/piranhaplant/spitter
//might snatch the code for that uranium ray for this since it should poison
name = "Piranha Spitter"
attack_armor_pen = 0
//Attack ranged settings.
ranged = 1 // Do I attack at range?
shoot_range = 6 // How far away do I start shooting from?
view_range = 5 //More range, more hurt, more... plant?
rapid = 1 // Three-round-burst fire mode
firing_lines = 0 // Avoids shooting allies
projectiletype = /obj/item/projectile/energy/piranhaspit // The projectiles I shoot
projectilesound = 'sound/weapons/thudswoosh.ogg' // The sound I make when I do it
ranged_ignore_incapitated = 1 //make it so our spitter doesnt stun lock dorks
ranged_cooldown_time = 90
//mob/living/simple_animal/hostile/piranhaplant/spitter/proc/Shoot()
//TOX/HALLOSS swap code goes here //TODO
//Piranha unique projectile
/obj/item/projectile/energy/piranhaspit
name = "piranha spit"
icon_state = "neurotoxin"
damage = 10
damage_type = HALLOSS
check_armour = "bio" //yup biohazard protection works here
flash_strength = 0
agony = 10
combustion = FALSE
/obj/item/weapon/reagent_containers/food/snacks/soylentgreen/piranha
name = "Soylent"
desc = "This was spat out by a strange plant that eats people."
icon_state = "soylent_green"
filling_color = "#B8E6B5"
center_of_mass = list("x"=15, "y"=11)
/obj/item/projectile/energy/piranhaspit/on_hit(var/atom/soyled)
if(prob(5))
soyl(soyled)
..()
/obj/item/projectile/energy/piranhaspit/proc/soyl(var/mob/M)
var/location = get_turf(M)
new /obj/item/weapon/reagent_containers/food/snacks/soylentgreen/piranha(location)
//VORE FLUFF section and extended gut settings
/mob/living/simple_animal/hostile/piranhaplant/init_vore()
..()
var/obj/belly/B = vore_selected
B.vore_verb = "chomp up"
B.name = "stomach"
B.desc = "You're pulled into the tight stomach of the plant. The walls knead weakly around you, coating you in thick, viscous fluids that cling to your body, that soon starts to tingle and burn..."
B.digest_burn = 0
B.digest_brute = 12
/mob/living/simple_animal/hostile/piranhaplant/pitcher
icon_state = "pitcher-plant"
icon_living = "pitcher-plant"
icon_dead = "pitcher-plant_dead"
name = "Pitcher Plant"
desc = "It's a plant! How pretty"
tt_desc = "Brig Flower"
health = 500
maxHealth = 500
var/antispam = 0
swallowTime = 3 SECONDS //If you get to close to a pitcher, its your own fault ;p
/mob/living/simple_animal/hostile/piranhaplant/pitcher/death()
..()
new /obj/item/weapon/reagent_containers/food/snacks/aesirsalad(location)
new /obj/item/weapon/reagent_containers/food/snacks/aesirsalad(location)
new /obj/item/weapon/reagent_containers/food/snacks/aesirsalad(location)
new /obj/item/weapon/reagent_containers/food/snacks/aesirsalad(location)
qdel(src)
/mob/living/simple_animal/hostile/piranhaplant/pitcher/Life()
..()
if(!anchored)
anchored=1
if(vore_fullness && !antispam)
antispam = 1
spawn(10)
if(bruteloss >= 1)
bruteloss -= 1
antispam = !antispam
if(prob(3))
new /obj/item/weapon/reagent_containers/food/snacks/soylentgreen/piranha(src.loc)
if(size_multiplier!=1*health/100 && health >= 50 && health <= 300)
size_multiplier=1*health/100
update_icons()
/mob/living/simple_animal/hostile/piranhaplant/pitcher/New()
..()
bruteloss = 400
/mob/living/simple_animal/hostile/piranhaplant/pitcher/init_vore()
..()
var/obj/belly/B = vore_selected
B.digest_burn = 0.5
B.digest_brute = 0
B.vore_verb = "slurp up"
B.name = "pitcher"
@@ -121,7 +121,8 @@
var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat.
var/minimum_distance = 1 //Minimum approach distance, so ranged mobs chase targets down, but still keep their distance set in tiles to the target, set higher to make mobs keep distance
var/move_shoot = 0
var/ranged_ignore_incapitated = 0 //Ranged mobs will by default keep shooting on unconscious targets, if set to 1 the mob will ignore unconscious victims.
//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
@@ -1270,19 +1271,24 @@
ai_log("AttackTarget() special",3)
if(SpecialAtkTarget()) //Might not succeed/be allowed, do something else.
return 1
//AAAAH!
if(distance <= 1)
ai_log("AttackTarget() melee",3)
PunchTarget()
return 1
//Open fire!
else if(ranged && (distance <= shoot_range) && ranged_cooldown <= world.time)
if(ishuman(target_mob) && ranged_ignore_incapitated)
var/mob/living/carbon/human/TA = target_mob
if(TA.stat == UNCONSCIOUS)
LoseTarget(TA)
return
ai_log("AttackTarget() ranged",3)
ShootTarget(target_mob)
return 1
else
ai_log("AttackTarget() out of range!",3)
stoplag(1) // Unfortunately this is needed to protect from ClosestDistance() sometimes not updating fast enough to prevent an infinite loop.
@@ -1777,5 +1783,5 @@
/mob/living/simple_animal/get_nametag_desc(mob/user)
return "<i>[tt_desc]</i>"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 10 KiB