mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-28 14:37:04 +01:00
Fix and tweak parrot behaviour (#13652)
* Fix and tweak parrot behaviour Parrot stolen item now renders under the parrot's icon to make it more clear when it holds something - remember to harm the parrot to make it drop the item Parrot adjacency checks now take windows into account - this fixes issues where the parrot would teleport behind a window to perch/steal an item Parrots no longer try to perch/steal an item in a turf that is inaccessible to them infinitely, causing them to be stuck Add emotes when parrot drops item Change default parrot name to not be improper Clean up code where possible Correct grammar errors in parrot messages Fix attacking parrots in harm mode not triggering specific behaviour * Code cleanup, CanAStarPassTo atom proc * Fix Travis * Address farie * Address farie 2 * fix atkverb confusion * use LAZYINITLIST * remove some out of scope defaults
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
//(i.e the max or the min dependant on the comparison function)
|
||||
/datum/heap/proc/Pop()
|
||||
if(!L.len)
|
||||
return 0
|
||||
return null
|
||||
. = L[1]
|
||||
|
||||
L[1] = L[L.len]
|
||||
@@ -36,8 +36,8 @@
|
||||
/datum/heap/proc/Swim(var/index)
|
||||
var/parent = round(index * 0.5)
|
||||
|
||||
while(parent > 0 && (call(cmp)(L[index],L[parent]) > 0))
|
||||
L.Swap(index,parent)
|
||||
while(parent > 0 && (call(cmp)(L[index], L[parent]) > 0))
|
||||
L.Swap(index, parent)
|
||||
index = parent
|
||||
parent = round(index * 0.5)
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
/datum/heap/proc/Sink(var/index)
|
||||
var/g_child = GetGreaterChild(index)
|
||||
|
||||
while(g_child > 0 && (call(cmp)(L[index],L[g_child]) < 0))
|
||||
L.Swap(index,g_child)
|
||||
while(g_child > 0 && (call(cmp)(L[index], L[g_child]) < 0))
|
||||
L.Swap(index, g_child)
|
||||
index = g_child
|
||||
g_child = GetGreaterChild(index)
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
if(index * 2 + 1 > L.len)
|
||||
return index * 2
|
||||
|
||||
if(call(cmp)(L[index * 2],L[index * 2 + 1]) < 0)
|
||||
if(call(cmp)(L[index * 2], L[index * 2 + 1]) < 0)
|
||||
return index * 2 + 1
|
||||
else
|
||||
return index * 2
|
||||
|
||||
+26
-22
@@ -62,24 +62,23 @@ Actual Adjacent procs :
|
||||
return b.f - a.f
|
||||
|
||||
//wrapper that returns an empty list if A* failed to find a path
|
||||
/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1)
|
||||
/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id = null, turf/exclude = null, simulated_only = TRUE)
|
||||
var/list/path = AStar(caller, end, dist, maxnodes, maxnodedepth, mintargetdist, adjacent, id, exclude, simulated_only)
|
||||
if(!path)
|
||||
path = list()
|
||||
return path
|
||||
|
||||
//the actual algorithm
|
||||
/proc/AStar(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1)
|
||||
|
||||
/proc/AStar(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id = null, turf/exclude = null, simulated_only = TRUE)
|
||||
//sanitation
|
||||
var/start = get_turf(caller)
|
||||
if(!start)
|
||||
return 0
|
||||
return null
|
||||
|
||||
if(maxnodes)
|
||||
//if start turf is farther than maxnodes from end turf, no need to do anything
|
||||
if(call(start, dist)(end) > maxnodes)
|
||||
return 0
|
||||
return null
|
||||
maxnodedepth = maxnodes //no need to consider path longer than maxnodes
|
||||
|
||||
var/datum/heap/open = new /datum/heap(/proc/HeapPathWeightCompare) //the open list
|
||||
@@ -92,7 +91,6 @@ Actual Adjacent procs :
|
||||
|
||||
//then run the main loop
|
||||
while(!open.IsEmpty() && !path)
|
||||
{
|
||||
//get the lower f node on the open list
|
||||
cur = open.Pop() //get the lower f turf in the open list
|
||||
closed.Add(cur.source) //and tell we've processed it
|
||||
@@ -100,7 +98,7 @@ Actual Adjacent procs :
|
||||
//if we only want to get near the target, check if we're close enough
|
||||
var/closeenough
|
||||
if(mintargetdist)
|
||||
closeenough = call(cur.source,dist)(end) <= mintargetdist
|
||||
closeenough = call(cur.source, dist)(end) <= mintargetdist
|
||||
|
||||
//if too many steps, abandon that path
|
||||
if(maxnodedepth && (cur.nt > maxnodedepth))
|
||||
@@ -118,12 +116,13 @@ Actual Adjacent procs :
|
||||
break
|
||||
|
||||
//get adjacents turfs using the adjacent proc, checking for access with id
|
||||
var/list/L = call(cur.source,adjacent)(caller, id, simulated_only)
|
||||
for(var/turf/T in L)
|
||||
var/list/L = call(cur.source, adjacent)(caller, id, simulated_only)
|
||||
for(var/t in L)
|
||||
var/turf/T = t
|
||||
if(T == exclude || (T in closed))
|
||||
continue
|
||||
|
||||
var/newg = cur.g + call(cur.source,dist)(T)
|
||||
var/newg = cur.g + call(cur.source, dist)(T)
|
||||
if(!T.PNode) //is not already in open list, so add it
|
||||
open.Insert(new /datum/pathnode(T,cur,newg,call(T,dist)(end),cur.nt+1))
|
||||
else //is already in open list, check if it's a better way from the current turf
|
||||
@@ -134,18 +133,17 @@ Actual Adjacent procs :
|
||||
T.PNode.nt = cur.nt + 1
|
||||
open.ReSort(T.PNode)//reorder the changed element in the list
|
||||
|
||||
}
|
||||
|
||||
//cleaning after us
|
||||
for(var/datum/pathnode/PN in open.L)
|
||||
PN.source.PNode = null
|
||||
for(var/turf/T in closed)
|
||||
for(var/t in closed)
|
||||
var/turf/T = t
|
||||
T.PNode = null
|
||||
|
||||
//reverse the path to get it from start to finish
|
||||
if(path)
|
||||
for(var/i = 1; i <= path.len/2; i++)
|
||||
path.Swap(i,path.len-i+1)
|
||||
for(var/i in 1 to path.len / 2)
|
||||
path.Swap(i, path.len - i + 1)
|
||||
|
||||
return path
|
||||
|
||||
@@ -156,10 +154,10 @@ Actual Adjacent procs :
|
||||
var/turf/simulated/T
|
||||
|
||||
for(var/dir in GLOB.cardinal)
|
||||
T = get_step(src,dir)
|
||||
T = get_step(src, dir)
|
||||
if(!T || (simulated_only && !istype(T)))
|
||||
continue
|
||||
if(!T.density && !LinkBlockedWithAccess(T,caller, ID))
|
||||
if(!T.density && !LinkBlockedWithAccess(T, caller, ID))
|
||||
L.Add(T)
|
||||
return L
|
||||
|
||||
@@ -170,15 +168,21 @@ Actual Adjacent procs :
|
||||
/turf/proc/LinkBlockedWithAccess(turf/T, caller, ID)
|
||||
var/adir = get_dir(src, T)
|
||||
var/rdir = get_dir(T, src)
|
||||
var/atom/caller_atom = caller
|
||||
if(!istype(caller_atom))
|
||||
caller_atom = null
|
||||
|
||||
for(var/obj/structure/window/W in src)
|
||||
if(!W.CanAStarPass(ID, adir))
|
||||
return 1
|
||||
return TRUE
|
||||
for(var/obj/machinery/door/window/W in src)
|
||||
if(!W.CanAStarPass(ID, adir))
|
||||
return 1
|
||||
return TRUE
|
||||
for(var/obj/O in T)
|
||||
if(!O.CanAStarPass(ID, rdir, caller))
|
||||
return 1
|
||||
var/pass_through = FALSE
|
||||
if(caller_atom)
|
||||
pass_through = caller_atom.CanAStarPassTo(ID, adir, O)
|
||||
if(!O.CanAStarPass(ID, rdir, caller) && !pass_through)
|
||||
return TRUE
|
||||
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
@@ -1066,6 +1066,13 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
|
||||
color = C
|
||||
return
|
||||
|
||||
/*
|
||||
Checks whether this atom can traverse the destination object when used as source for AStar.
|
||||
This should only be used as an override to /obj/proc/CanAStarPass. Aka don't use this unless you can't change the object's proc.
|
||||
Returning TRUE here will override the above proc's result.
|
||||
*/
|
||||
/atom/proc/CanAStarPassTo(ID, dir, obj/destination)
|
||||
return TRUE
|
||||
|
||||
/** Call this when you want to present a renaming prompt to the user.
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
|
||||
can_be_hit = FALSE
|
||||
suicidal_hands = TRUE
|
||||
|
||||
var/list/attack_verb //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]"
|
||||
var/hitsound = null
|
||||
var/usesound = null
|
||||
var/list/attack_verb //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]"
|
||||
var/throwhitsound
|
||||
var/w_class = WEIGHT_CLASS_NORMAL
|
||||
var/slot_flags = 0 //This is used to determine on which slots an item can fit.
|
||||
@@ -115,6 +115,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
|
||||
hitsound = 'sound/items/welder.ogg'
|
||||
if(damtype == "brute")
|
||||
hitsound = "swing_hit"
|
||||
LAZYINITLIST(attack_verb)
|
||||
if(!move_resist)
|
||||
determine_move_resist()
|
||||
|
||||
|
||||
@@ -14,45 +14,45 @@
|
||||
|
||||
//Only a maximum of one action and one intent should be active at any given time.
|
||||
//Actions
|
||||
#define PARROT_PERCH 1 //Sitting/sleeping, not moving
|
||||
#define PARROT_SWOOP 2 //Moving towards or away from a target
|
||||
#define PARROT_WANDER 4 //Moving without a specific target in mind
|
||||
#define PARROT_PERCH (1<<0) //Sitting/sleeping, not moving
|
||||
#define PARROT_SWOOP (1<<1) //Moving towards or away from a target
|
||||
#define PARROT_WANDER (1<<2) //Moving without a specific target in mind
|
||||
|
||||
//Intents
|
||||
#define PARROT_STEAL 8 //Flying towards a target to steal it/from it
|
||||
#define PARROT_ATTACK 16 //Flying towards a target to attack it
|
||||
#define PARROT_RETURN 32 //Flying towards its perch
|
||||
#define PARROT_FLEE 64 //Flying away from its attacker
|
||||
|
||||
#define PARROT_STEAL (1<<3) //Flying towards a target to steal it/from it
|
||||
#define PARROT_ATTACK (1<<4) //Flying towards a target to attack it
|
||||
#define PARROT_RETURN (1<<5) //Flying towards its perch
|
||||
#define PARROT_FLEE (1<<6) //Flying away from its attacker
|
||||
|
||||
/mob/living/simple_animal/parrot
|
||||
name = "\improper Parrot"
|
||||
desc = "The parrot squaks, \"It's a Parrot! BAWWK!\""
|
||||
name = "parrot"
|
||||
desc = "The parrot squaks, \"It's a parrot! BAWWK!\""
|
||||
icon = 'icons/mob/animal.dmi'
|
||||
icon_state = "parrot_fly"
|
||||
icon_living = "parrot_fly"
|
||||
icon_dead = "parrot_dead"
|
||||
pass_flags = PASSTABLE
|
||||
can_collar = 1
|
||||
can_collar = TRUE
|
||||
|
||||
var/list/clean_speak = list(
|
||||
"Hi",
|
||||
"Hello!",
|
||||
"Cracker?",
|
||||
"BAWWWWK george mellons griffing me")
|
||||
speak_emote = list("squawks","says","yells")
|
||||
emote_hear = list("squawks","bawks")
|
||||
"BAWWWWK george mellons griffing me"
|
||||
)
|
||||
speak_emote = list("squawks", "says", "yells")
|
||||
emote_hear = list("squawks", "bawks")
|
||||
emote_see = list("flutters its wings")
|
||||
|
||||
speak_chance = 1//1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s
|
||||
turns_per_move = 5
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/cracker = 3)
|
||||
|
||||
response_help = "pets the"
|
||||
response_disarm = "gently moves aside the"
|
||||
response_harm = "swats the"
|
||||
stop_automated_movement = 1
|
||||
universal_speak = 1
|
||||
response_help = "pets"
|
||||
response_disarm = "gently moves aside"
|
||||
response_harm = "swats"
|
||||
stop_automated_movement = TRUE
|
||||
universal_speak = TRUE
|
||||
mob_size = MOB_SIZE_SMALL
|
||||
|
||||
var/parrot_state = PARROT_WANDER //Hunt for a perch when created
|
||||
@@ -63,8 +63,8 @@
|
||||
var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower.
|
||||
var/parrot_been_shot = 0 //Parrots get a speed bonus after being shot. This will deincrement every process_ai() and at 0 the parrot will return to regular speed.
|
||||
|
||||
var/list/speech_buffer = list()
|
||||
var/list/available_channels = list()
|
||||
var/list/speech_buffer
|
||||
var/list/available_channels
|
||||
|
||||
//Headset for Poly to yell at engineers :)
|
||||
var/obj/item/radio/headset/ears = null
|
||||
@@ -78,7 +78,7 @@
|
||||
var/obj/parrot_perch = null
|
||||
var/obj/desired_perches = list(/obj/structure/computerframe, /obj/structure/displaycase, \
|
||||
/obj/structure/filingcabinet, /obj/machinery/teleport, \
|
||||
/obj/machinery/suit_storage_unit, /obj/machinery/clonepod, \
|
||||
/obj/machinery/suit_storage_unit, /obj/machinery/clonepod, \
|
||||
/obj/machinery/dna_scannernew, /obj/machinery/tcomms, \
|
||||
/obj/machinery/nuclearbomb, /obj/machinery/particle_accelerator, \
|
||||
/obj/machinery/recharge_station, /obj/machinery/smartfridge, \
|
||||
@@ -89,9 +89,10 @@
|
||||
flying = TRUE
|
||||
gold_core_spawnable = FRIENDLY_SPAWN
|
||||
|
||||
|
||||
/mob/living/simple_animal/parrot/New()
|
||||
..()
|
||||
speech_buffer = list()
|
||||
available_channels = list()
|
||||
GLOB.hear_radio_list += src
|
||||
if(!ears)
|
||||
var/headset = pick(/obj/item/radio/headset/headset_sec, \
|
||||
@@ -116,9 +117,9 @@
|
||||
/mob/living/simple_animal/parrot/death(gibbed)
|
||||
if(can_die())
|
||||
if(held_item)
|
||||
held_item.loc = src.loc
|
||||
held_item = null
|
||||
walk(src,0)
|
||||
custom_emote(EMOTE_VISUAL, "lets go of [held_item]!")
|
||||
drop_held_item()
|
||||
walk(src, 0)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/parrot/Stat()
|
||||
@@ -128,7 +129,7 @@
|
||||
/*
|
||||
* Inventory
|
||||
*/
|
||||
/mob/living/simple_animal/parrot/show_inv(mob/user as mob)
|
||||
/mob/living/simple_animal/parrot/show_inv(mob/user)
|
||||
user.set_machine(src)
|
||||
|
||||
var/dat = {"<table>"}
|
||||
@@ -147,9 +148,8 @@
|
||||
popup.open()
|
||||
|
||||
/mob/living/simple_animal/parrot/Topic(href, href_list)
|
||||
|
||||
//Can the usr physically do this?
|
||||
if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr))
|
||||
if(!usr.canmove || usr.stat || usr.restrained() || !usr.Adjacent(src))
|
||||
return
|
||||
|
||||
//Is the usr's mob type able to do this?
|
||||
@@ -185,7 +185,7 @@
|
||||
if(!item_to_add)
|
||||
return
|
||||
|
||||
if( !istype(item_to_add, /obj/item/radio/headset) )
|
||||
if(!istype(item_to_add, /obj/item/radio/headset))
|
||||
to_chat(usr, "<span class='warning'>This object won't fit.</span>")
|
||||
return
|
||||
|
||||
@@ -221,16 +221,16 @@
|
||||
else
|
||||
..()
|
||||
|
||||
|
||||
/*
|
||||
* Attack responces
|
||||
*/
|
||||
//Humans, monkeys, aliens
|
||||
/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/M as mob)
|
||||
/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/M)
|
||||
..()
|
||||
if(client) return
|
||||
if(!stat && M.a_intent == "hurt")
|
||||
if(client)
|
||||
return
|
||||
|
||||
if(!stat && M.a_intent == "harm")
|
||||
icon_state = "parrot_fly" //It is going to be flying regardless of whether it flees or attacks
|
||||
|
||||
if(parrot_state == PARROT_PERCH)
|
||||
@@ -242,12 +242,15 @@
|
||||
if(M.health < 50) //Weakened mob? Fight back!
|
||||
parrot_state |= PARROT_ATTACK
|
||||
else
|
||||
if(held_item)
|
||||
custom_emote(EMOTE_VISUAL, "lets go of [held_item]!")
|
||||
|
||||
parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell!
|
||||
drop_held_item(0)
|
||||
drop_held_item(FALSE)
|
||||
return
|
||||
|
||||
//Mobs with objects
|
||||
/mob/living/simple_animal/parrot/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
|
||||
/mob/living/simple_animal/parrot/attackby(obj/item/O, mob/user, params)
|
||||
..()
|
||||
if(!stat && !client && !istype(O, /obj/item/stack/medical))
|
||||
if(O.force)
|
||||
@@ -255,13 +258,13 @@
|
||||
parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
|
||||
|
||||
parrot_interest = user
|
||||
parrot_state = PARROT_SWOOP | PARROT_FLEE
|
||||
parrot_state = PARROT_SWOOP|PARROT_FLEE
|
||||
icon_state = "parrot_fly"
|
||||
drop_held_item(0)
|
||||
drop_held_item(FALSE)
|
||||
return
|
||||
|
||||
//Bullets
|
||||
/mob/living/simple_animal/parrot/bullet_act(var/obj/item/projectile/Proj)
|
||||
/mob/living/simple_animal/parrot/bullet_act(obj/item/projectile/P)
|
||||
..()
|
||||
if(!stat && !client)
|
||||
if(parrot_state == PARROT_PERCH)
|
||||
@@ -271,10 +274,9 @@
|
||||
parrot_state = PARROT_WANDER //OWFUCK, Been shot! RUN LIKE HELL!
|
||||
parrot_been_shot += 5
|
||||
icon_state = "parrot_fly"
|
||||
drop_held_item(0)
|
||||
drop_held_item(FALSE)
|
||||
return
|
||||
|
||||
|
||||
/*
|
||||
* AI - Not really intelligent, but I'm calling it AI anyway.
|
||||
*/
|
||||
@@ -302,10 +304,9 @@
|
||||
parrot_state = PARROT_WANDER
|
||||
return
|
||||
|
||||
if(!isturf(src.loc) || !canmove || buckled)
|
||||
if(!isturf(loc) || !canmove || buckled)
|
||||
return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove)
|
||||
|
||||
|
||||
//-----SPEECH
|
||||
/* Parrot speech mimickry!
|
||||
Phrases that the parrot hears in mob/living/say() get added to speach_buffer.
|
||||
@@ -318,12 +319,11 @@
|
||||
clean_speak += pick(speech_buffer)
|
||||
speech_buffer.Cut()
|
||||
|
||||
|
||||
//-----SLEEPING
|
||||
if(parrot_state == PARROT_PERCH)
|
||||
if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasnt moved our perch on us
|
||||
if(parrot_perch && parrot_perch.loc != loc) //Make sure someone hasnt moved our perch on us
|
||||
if(parrot_perch in view(src))
|
||||
parrot_state = PARROT_SWOOP | PARROT_RETURN
|
||||
parrot_state = PARROT_SWOOP|PARROT_RETURN
|
||||
icon_state = "parrot_fly"
|
||||
return
|
||||
else
|
||||
@@ -333,7 +333,6 @@
|
||||
|
||||
if(--parrot_sleep_dur) //Zzz
|
||||
return
|
||||
|
||||
else
|
||||
//This way we only call the stuff below once every [sleep_max] ticks.
|
||||
parrot_sleep_dur = parrot_sleep_max
|
||||
@@ -344,8 +343,8 @@
|
||||
//Search for item to steal
|
||||
parrot_interest = search_for_item()
|
||||
if(parrot_interest)
|
||||
custom_emote(1,"looks in [parrot_interest]'s direction and takes flight.")
|
||||
parrot_state = PARROT_SWOOP | PARROT_STEAL
|
||||
custom_emote(EMOTE_VISUAL, "looks in [parrot_interest]'s direction and takes flight.")
|
||||
parrot_state = PARROT_SWOOP|PARROT_STEAL
|
||||
icon_state = "parrot_fly"
|
||||
return
|
||||
|
||||
@@ -366,88 +365,98 @@
|
||||
if(AM)
|
||||
if(istype(AM, /obj/item) || isliving(AM)) //If stealable item
|
||||
parrot_interest = AM
|
||||
custom_emote(1,"turns and flies towards [parrot_interest]")
|
||||
parrot_state = PARROT_SWOOP | PARROT_STEAL
|
||||
parrot_state = PARROT_SWOOP|PARROT_STEAL
|
||||
face_atom(AM)
|
||||
custom_emote(EMOTE_VISUAL, "turns and flies towards [parrot_interest].")
|
||||
return
|
||||
else //Else it's a perch
|
||||
parrot_perch = AM
|
||||
parrot_state = PARROT_SWOOP | PARROT_RETURN
|
||||
parrot_state = PARROT_SWOOP|PARROT_RETURN
|
||||
return
|
||||
return
|
||||
|
||||
if(parrot_interest && (parrot_interest in view(src)))
|
||||
parrot_state = PARROT_SWOOP | PARROT_STEAL
|
||||
parrot_state = PARROT_SWOOP|PARROT_STEAL
|
||||
return
|
||||
|
||||
if(parrot_perch && (parrot_perch in view(src)))
|
||||
parrot_state = PARROT_SWOOP | PARROT_RETURN
|
||||
parrot_state = PARROT_SWOOP|PARROT_RETURN
|
||||
return
|
||||
|
||||
else //Have an item but no perch? Find one!
|
||||
parrot_perch = search_for_perch()
|
||||
if(parrot_perch)
|
||||
parrot_state = PARROT_SWOOP | PARROT_RETURN
|
||||
parrot_state = PARROT_SWOOP|PARROT_RETURN
|
||||
return
|
||||
//-----STEALING
|
||||
else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL))
|
||||
walk(src,0)
|
||||
if(!parrot_interest || held_item)
|
||||
parrot_state = PARROT_SWOOP | PARROT_RETURN
|
||||
else if(parrot_state == (PARROT_SWOOP|PARROT_STEAL))
|
||||
walk(src, 0)
|
||||
|
||||
if(!parrot_interest || held_item || !(parrot_interest in view(src)))
|
||||
parrot_state = PARROT_SWOOP|PARROT_RETURN
|
||||
return
|
||||
|
||||
if(!(parrot_interest in view(src)))
|
||||
parrot_state = PARROT_SWOOP | PARROT_RETURN
|
||||
return
|
||||
|
||||
if(in_range(src, parrot_interest))
|
||||
|
||||
if(Adjacent(parrot_interest))
|
||||
if(isliving(parrot_interest))
|
||||
steal_from_mob()
|
||||
|
||||
else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch
|
||||
if(!parrot_perch || parrot_interest.loc != parrot_perch.loc)
|
||||
held_item = parrot_interest
|
||||
parrot_interest.loc = src
|
||||
visible_message("[src] grabs the [held_item]!", "<span class='notice'>You grab the [held_item]!</span>", "You hear the sounds of wings flapping furiously.")
|
||||
try_grab_item(parrot_interest)
|
||||
visible_message("<span class='notice'>[src] grabs [held_item]!</span>", "<span class='notice'>You grab [held_item]!</span>", "You hear the sounds of wings flapping furiously.")
|
||||
|
||||
parrot_interest = null
|
||||
parrot_state = PARROT_SWOOP | PARROT_RETURN
|
||||
parrot_state = PARROT_SWOOP|PARROT_RETURN
|
||||
return
|
||||
|
||||
walk_to(src, parrot_interest, 1, parrot_speed)
|
||||
var/list/path_to_take = get_path_to(src, get_turf(parrot_interest), /turf/proc/Distance_cardinal)
|
||||
if(length(path_to_take) <= 1) // The target is below us
|
||||
parrot_interest = null
|
||||
parrot_state = PARROT_SWOOP|PARROT_RETURN
|
||||
return
|
||||
|
||||
walk_to(src, path_to_take[2], 0, parrot_speed)
|
||||
return
|
||||
|
||||
//-----RETURNING TO PERCH
|
||||
else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN))
|
||||
else if(parrot_state == (PARROT_SWOOP|PARROT_RETURN))
|
||||
walk(src, 0)
|
||||
|
||||
if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else.
|
||||
parrot_perch = null
|
||||
parrot_state = PARROT_WANDER
|
||||
return
|
||||
|
||||
if(in_range(src, parrot_perch))
|
||||
src.loc = parrot_perch.loc
|
||||
if(Adjacent(parrot_perch))
|
||||
forceMove(parrot_perch.loc)
|
||||
drop_held_item()
|
||||
parrot_state = PARROT_PERCH
|
||||
icon_state = "parrot_sit"
|
||||
return
|
||||
|
||||
walk_to(src, parrot_perch, 1, parrot_speed)
|
||||
var/list/path_to_take = get_path_to(src, get_turf(parrot_perch), /turf/proc/Distance_cardinal)
|
||||
if(length(path_to_take) <= 1) // The target is below us
|
||||
parrot_perch = null
|
||||
parrot_state = PARROT_WANDER
|
||||
return
|
||||
|
||||
walk_to(src, path_to_take[2], 0, parrot_speed)
|
||||
return
|
||||
|
||||
//-----FLEEING
|
||||
else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE))
|
||||
walk(src,0)
|
||||
if(!parrot_interest || !isliving(parrot_interest)) //Sanity
|
||||
parrot_state = PARROT_WANDER
|
||||
else if(parrot_state == (PARROT_SWOOP|PARROT_FLEE))
|
||||
walk(src, 0)
|
||||
|
||||
walk_away(src, parrot_interest, 1, parrot_speed-parrot_been_shot)
|
||||
if(!parrot_interest || !isliving(parrot_interest) || !Adjacent(parrot_interest)) //Sanity
|
||||
parrot_state = PARROT_WANDER
|
||||
parrot_interest = null
|
||||
return
|
||||
|
||||
walk_away(src, parrot_interest, 0, parrot_speed - parrot_been_shot)
|
||||
parrot_been_shot--
|
||||
return
|
||||
|
||||
//-----ATTACKING
|
||||
else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK))
|
||||
|
||||
else if(parrot_state == (PARROT_SWOOP|PARROT_ATTACK))
|
||||
//If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander
|
||||
if(!parrot_interest || !isliving(parrot_interest))
|
||||
parrot_interest = null
|
||||
@@ -457,8 +466,7 @@
|
||||
var/mob/living/L = parrot_interest
|
||||
|
||||
//If the mob is close enough to interact with
|
||||
if(in_range(src, parrot_interest))
|
||||
|
||||
if(Adjacent(parrot_interest))
|
||||
//If the mob we've been chasing/attacking dies or falls into crit, check for loot!
|
||||
if(L.stat)
|
||||
parrot_interest = null
|
||||
@@ -466,34 +474,34 @@
|
||||
held_item = steal_from_ground()
|
||||
if(!held_item)
|
||||
held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances.
|
||||
update_held_icon()
|
||||
if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home
|
||||
parrot_state = PARROT_SWOOP | PARROT_RETURN
|
||||
parrot_state = PARROT_SWOOP|PARROT_RETURN
|
||||
else
|
||||
parrot_state = PARROT_WANDER
|
||||
return
|
||||
|
||||
//Time for the hurt to begin!
|
||||
var/damage = rand(5,10)
|
||||
var/damage = rand(5, 10)
|
||||
|
||||
if(ishuman(parrot_interest))
|
||||
var/mob/living/carbon/human/H = parrot_interest
|
||||
var/obj/item/organ/external/affecting = H.get_organ(ran_zone(pick(parrot_dam_zone)))
|
||||
|
||||
H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp = 1)
|
||||
custom_emote(1, pick("pecks [H]'s [affecting].", "cuts [H]'s [affecting] with its talons."))
|
||||
|
||||
H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp = TRUE)
|
||||
custom_emote(EMOTE_VISUAL, pick("pecks [H]'s [affecting].", "cuts [H]'s [affecting] with its talons."))
|
||||
else
|
||||
L.adjustBruteLoss(damage)
|
||||
custom_emote(1, pick("pecks at [L].", "claws [L]."))
|
||||
custom_emote(EMOTE_VISUAL, pick("pecks at [L].", "claws [L]."))
|
||||
return
|
||||
|
||||
//Otherwise, fly towards the mob!
|
||||
else
|
||||
// No AStar here because the parrot is pissed and isn't thinking rationally.
|
||||
walk_to(src, parrot_interest, 1, parrot_speed)
|
||||
return
|
||||
//-----STATE MISHAP
|
||||
else //This should not happen. If it does lets reset everything and try again
|
||||
walk(src,0)
|
||||
walk(src, 0)
|
||||
parrot_interest = null
|
||||
parrot_perch = null
|
||||
drop_held_item()
|
||||
@@ -516,6 +524,10 @@
|
||||
if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src)
|
||||
continue
|
||||
|
||||
// Can we find a path to it?
|
||||
if(loc != AM.loc && !length(get_path_to(src, get_turf(AM), /turf/proc/Distance_cardinal)))
|
||||
continue
|
||||
|
||||
if(istype(AM, /obj/item))
|
||||
var/obj/item/I = AM
|
||||
if(I.w_class < WEIGHT_CLASS_SMALL)
|
||||
@@ -529,6 +541,10 @@
|
||||
|
||||
/mob/living/simple_animal/parrot/proc/search_for_perch()
|
||||
for(var/obj/O in view(src))
|
||||
// Can we find a path to it?
|
||||
if(loc != O.loc && !length(get_path_to(src, get_turf(O), /turf/proc/Distance_cardinal)))
|
||||
continue
|
||||
|
||||
for(var/path in desired_perches)
|
||||
if(istype(O, path))
|
||||
return O
|
||||
@@ -537,6 +553,10 @@
|
||||
//This proc was made to save on doing two 'in view' loops seperatly
|
||||
/mob/living/simple_animal/parrot/proc/search_for_perch_and_item()
|
||||
for(var/atom/movable/AM in view(src))
|
||||
// Can we find a path to it?
|
||||
if(loc != AM.loc && !length(get_path_to(src, get_turf(AM), /turf/proc/Distance_cardinal)))
|
||||
continue
|
||||
|
||||
for(var/perch_path in desired_perches)
|
||||
if(istype(AM, perch_path))
|
||||
return AM
|
||||
@@ -556,7 +576,6 @@
|
||||
return C
|
||||
return null
|
||||
|
||||
|
||||
/*
|
||||
* Verbs - These are actually procs, but can be used as verbs by player-controlled parrots.
|
||||
*/
|
||||
@@ -569,20 +588,18 @@
|
||||
return -1
|
||||
|
||||
if(held_item)
|
||||
to_chat(src, "<span class='warning'>You are already holding the [held_item]</span>")
|
||||
to_chat(src, "<span class='warning'>You are already holding [held_item]</span>")
|
||||
return 1
|
||||
|
||||
for(var/obj/item/I in view(1,src))
|
||||
for(var/obj/item/I in view(1, src))
|
||||
//Make sure we're not already holding it and it's small enough
|
||||
if(I.loc != src && I.w_class <= WEIGHT_CLASS_SMALL)
|
||||
|
||||
//If we have a perch and the item is sitting on it, continue
|
||||
if(!client && parrot_perch && I.loc == parrot_perch.loc)
|
||||
continue
|
||||
|
||||
held_item = I
|
||||
I.loc = src
|
||||
visible_message("[src] grabs the [held_item]!", "<span class='notice'>You grab the [held_item]!</span>", "You hear the sounds of wings flapping furiously.")
|
||||
try_grab_item(I)
|
||||
visible_message("<span class='notice'>[src] grabs [held_item]!</span>", "<span class='notice'>You grab [held_item]!</span>", "You hear the sounds of wings flapping furiously.")
|
||||
return held_item
|
||||
|
||||
to_chat(src, "<span class = 'warning'>There is nothing of interest to take.</span>")
|
||||
@@ -597,23 +614,21 @@
|
||||
return -1
|
||||
|
||||
if(held_item)
|
||||
to_chat(src, "<span class='warning'>You are already holding the [held_item]</span>")
|
||||
to_chat(src, "<span class='warning'>You are already holding [held_item]</span>")
|
||||
return 1
|
||||
|
||||
var/obj/item/stolen_item = null
|
||||
|
||||
for(var/mob/living/carbon/C in view(1,src))
|
||||
for(var/mob/living/carbon/C in view(1, src))
|
||||
if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL)
|
||||
stolen_item = C.l_hand
|
||||
|
||||
if(C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL)
|
||||
stolen_item = C.r_hand
|
||||
|
||||
if(stolen_item)
|
||||
C.unEquip(stolen_item)
|
||||
held_item = stolen_item
|
||||
stolen_item.loc = src
|
||||
visible_message("[src] grabs the [held_item] out of [C]'s hand!", "<span class='notice'>You snag the [held_item] out of [C]'s hand!</span>", "You hear the sounds of wings flapping furiously.")
|
||||
if(stolen_item && C.unEquip(stolen_item))
|
||||
try_grab_item(stolen_item)
|
||||
visible_message("<span class='notice'>[src] grabs [held_item] out of [C]'s hand!</span>", "<span class='notice'>You snag [held_item] out of [C]'s hand!</span>", "You hear the sounds of wings flapping furiously.")
|
||||
return held_item
|
||||
|
||||
to_chat(src, "<span class='warning'>There is nothing of interest to take.</span>")
|
||||
@@ -627,11 +642,10 @@
|
||||
if(stat)
|
||||
return
|
||||
|
||||
src.drop_held_item()
|
||||
|
||||
drop_held_item()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/parrot/proc/drop_held_item(var/drop_gently = 1)
|
||||
/mob/living/simple_animal/parrot/proc/drop_held_item(drop_gently = TRUE)
|
||||
set name = "Drop held item"
|
||||
set category = "Parrot"
|
||||
set desc = "Drop the item you're holding."
|
||||
@@ -646,16 +660,18 @@
|
||||
if(!drop_gently)
|
||||
if(istype(held_item, /obj/item/grenade))
|
||||
var/obj/item/grenade/G = held_item
|
||||
G.loc = src.loc
|
||||
G.forceMove(loc)
|
||||
G.prime()
|
||||
to_chat(src, "You let go of the [held_item]!")
|
||||
to_chat(src, "You let go of [held_item]!")
|
||||
held_item = null
|
||||
update_held_icon()
|
||||
return 1
|
||||
|
||||
to_chat(src, "You drop the [held_item].")
|
||||
to_chat(src, "You drop [held_item].")
|
||||
|
||||
held_item.loc = src.loc
|
||||
held_item.forceMove(loc)
|
||||
held_item = null
|
||||
update_held_icon()
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/parrot/proc/perch_player()
|
||||
@@ -667,15 +683,30 @@
|
||||
return
|
||||
|
||||
if(icon_state == "parrot_fly")
|
||||
for(var/atom/movable/AM in view(src,1))
|
||||
for(var/atom/movable/AM in view(src, 1))
|
||||
for(var/perch_path in desired_perches)
|
||||
if(istype(AM, perch_path))
|
||||
src.loc = AM.loc
|
||||
forceMove(AM.loc)
|
||||
icon_state = "parrot_sit"
|
||||
return
|
||||
to_chat(src, "<span class='warning'>There is no perch nearby to sit on.</span>")
|
||||
return
|
||||
|
||||
/**
|
||||
* Attempts to pick up an adjacent item
|
||||
*
|
||||
* Arguments:
|
||||
* * I - The item to try and pick up
|
||||
*/
|
||||
/mob/living/simple_animal/parrot/proc/try_grab_item(obj/I)
|
||||
if(!Adjacent(I))
|
||||
return
|
||||
if(held_item)
|
||||
drop_held_item()
|
||||
held_item = I
|
||||
update_held_icon()
|
||||
I.forceMove(src)
|
||||
|
||||
/*
|
||||
* Sub-types
|
||||
*/
|
||||
@@ -706,7 +737,7 @@
|
||||
available_channels = list(":e")
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/parrot/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios)
|
||||
/mob/living/simple_animal/parrot/handle_message_mode(message_mode, list/message_pieces, verb, used_radios)
|
||||
if(message_mode && istype(ears))
|
||||
ears.talk_into(src, message_pieces, message_mode, verb)
|
||||
used_radios += ears
|
||||
@@ -716,15 +747,30 @@
|
||||
parrot_hear(html_decode(multilingual_to_message(message_pieces)))
|
||||
..()
|
||||
|
||||
|
||||
|
||||
/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, var/verb="says", var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target)
|
||||
/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, verb = "says", part_a, part_b, mob/speaker = null, hard_to_hear = 0, atom/follow_target)
|
||||
if(speaker != src && prob(50))
|
||||
parrot_hear(html_decode(multilingual_to_message(message_pieces)))
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/parrot/proc/parrot_hear(var/message="")
|
||||
/mob/living/simple_animal/parrot/proc/parrot_hear(message)
|
||||
if(!message || stat)
|
||||
return
|
||||
speech_buffer.Add(message)
|
||||
|
||||
/mob/living/simple_animal/parrot/proc/update_held_icon()
|
||||
underlays.Cut()
|
||||
|
||||
if(!held_item)
|
||||
return
|
||||
|
||||
var/matrix/m180 = matrix(held_item.transform)
|
||||
m180.Turn(180)
|
||||
|
||||
var/held_item_icon = image(held_item, pixel_y = -8)
|
||||
animate(held_item_icon, transform = m180)
|
||||
underlays += held_item_icon
|
||||
|
||||
/mob/living/simple_animal/parrot/CanAStarPassTo(ID, dir, obj/destination)
|
||||
for(var/path in desired_perches)
|
||||
if(istype(destination, path))
|
||||
return TRUE
|
||||
|
||||
Reference in New Issue
Block a user