diff --git a/code/__HELPERS/heap.dm b/code/__HELPERS/heap.dm index b878969571e..50a11e71abb 100644 --- a/code/__HELPERS/heap.dm +++ b/code/__HELPERS/heap.dm @@ -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 diff --git a/code/defines/procs/AStar.dm b/code/defines/procs/AStar.dm index 1d6fa5619e2..e2212ed8fd3 100644 --- a/code/defines/procs/AStar.dm +++ b/code/defines/procs/AStar.dm @@ -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 diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 74fc4b12458..67675b24449 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -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. diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 5052874f5cc..138d156040a 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -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() diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index e2eee66e4aa..73a08ef2bfd 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -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 = {""} @@ -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, "This object won't fit.") 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]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") + try_grab_item(parrot_interest) + visible_message("[src] grabs [held_item]!", "You grab [held_item]!", "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, "You are already holding the [held_item]") + to_chat(src, "You are already holding [held_item]") 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]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") + try_grab_item(I) + visible_message("[src] grabs [held_item]!", "You grab [held_item]!", "You hear the sounds of wings flapping furiously.") return held_item to_chat(src, "There is nothing of interest to take.") @@ -597,23 +614,21 @@ return -1 if(held_item) - to_chat(src, "You are already holding the [held_item]") + to_chat(src, "You are already holding [held_item]") 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!", "You snag the [held_item] out of [C]'s hand!", "You hear the sounds of wings flapping furiously.") + if(stolen_item && C.unEquip(stolen_item)) + try_grab_item(stolen_item) + visible_message("[src] grabs [held_item] out of [C]'s hand!", "You snag [held_item] out of [C]'s hand!", "You hear the sounds of wings flapping furiously.") return held_item to_chat(src, "There is nothing of interest to take.") @@ -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, "There is no perch nearby to sit on.") 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