Basic Mob Gorillas (#78918)

## About The Pull Request

Now we can make basic mobs with hands easily so I did, they don't
actually use their hands for anything with AI.
In the future we can come back and share the monkey AI where they pick
up items to hit people with, but frankly few weapons are more deadly
than a gorilla's fists.

IIRC I didn't really change their behaviour much, this is mostly just a
straight conversion. Main difference is that they will prioritise eating
nearby bananas and fruit salads over punching people.

When I make these conversions nowadays I need to decide between "does
this attack at the speed that it did as an NPC mob or the speed it did
as a player?"
I am arbitrarily deciding that gorillas are usually not players and
electing for the former, but tell me if you disagree.

I also made "show basic inhand sprites" into a component shared by
Gorillas, Drones, and Dextrous Guardians (all also now available to
become basic, once I get around to it),

And I added an AI behaviour to run a basic emote. This is similar but
different to "random speech", which kind of sucks and needs rewriting
anyway.
Gorillas don't speak, only ooga.

## Why It's Good For The Game

https://www.youtube.com/watch?v=npuuTBlEb1U

## Changelog

🆑
refactor: Gorillas now use the basic mob framework. Please report any
unusual side effects.
/🆑
This commit is contained in:
Jacquerel
2023-10-11 23:58:29 +01:00
committed by GitHub
parent 02ea1e8ceb
commit 504e6acfa3
38 changed files with 372 additions and 344 deletions

View File

@@ -11,6 +11,11 @@
///How close a mob must be for us to select it as a target, if that is less than how far we can maintain it as a target
#define BB_AGGRO_RANGE "BB_aggro_range"
/// Store a single or list of emotes at this key
#define BB_EMOTE_KEY "BB_emotes"
/// Chance to perform an emote per second
#define BB_EMOTE_CHANCE "BB_EMOTE_CHANCE"
///Turf we want a mob to move to
#define BB_TRAVEL_DESTINATION "BB_travel_destination"
@@ -105,4 +110,4 @@
#define BB_EMOTE_HEAR "emote_hear"
#define BB_EMOTE_SEE "emote_see"
#define BB_EMOTE_SOUND "emote_sound"
#define BB_EMOTE_CHANCE "emote_chance"
#define BB_SPEAK_CHANCE "emote_chance"

View File

@@ -31,6 +31,9 @@
/// From base of /mob/living/simple_animal/bot/proc/bot_step()
#define COMSIG_MOB_BOT_STEP "mob_bot_step"
/// From base of /mob/proc/update_held_items
#define COMSIG_MOB_UPDATE_HELD_ITEMS "mob_update_held_items"
/// From base of /client/Move(): (list/move_args)
#define COMSIG_MOB_CLIENT_PRE_LIVING_MOVE "mob_client_pre_living_move"
/// Should we stop the current living movement attempt

View File

@@ -2,9 +2,8 @@
/// If drones are blacklisted from certain sensitive machines
GLOBAL_VAR_INIT(drone_machine_blacklist_enabled, FALSE)
#define DRONE_HANDS_LAYER 1
#define DRONE_HEAD_LAYER 2
#define DRONE_TOTAL_LAYERS 2
#define DRONE_HEAD_LAYER 1
#define DRONE_TOTAL_LAYERS 1
/// Message displayed when new drone spawns in drone network
#define DRONE_NET_CONNECT span_notice("DRONE NETWORK: [name] connected.")

View File

@@ -3,6 +3,5 @@
#define GUARDIAN_THEME_CARP "carp"
#define GUARDIAN_THEME_MINER "miner"
#define GUARDIAN_COLOR_LAYER 2
#define GUARDIAN_HANDS_LAYER 1
#define GUARDIAN_TOTAL_LAYERS 2
#define GUARDIAN_COLOR_LAYER 1
#define GUARDIAN_TOTAL_LAYERS 1

View File

@@ -0,0 +1,33 @@
/// Intermittently run an emote
/datum/ai_planning_subtree/run_emote
var/emote_key = BB_EMOTE_KEY
var/emote_chance_key = BB_EMOTE_CHANCE
/datum/ai_planning_subtree/run_emote/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/emote_chance = controller.blackboard[emote_chance_key] || 0
if (!SPT_PROB(emote_chance, seconds_per_tick))
return
controller.queue_behavior(/datum/ai_behavior/run_emote, emote_key)
/// Emote from a blackboard key
/datum/ai_behavior/run_emote
/datum/ai_behavior/run_emote/perform(seconds_per_tick, datum/ai_controller/controller, emote_key)
var/mob/living/living_pawn = controller.pawn
if (!isliving(living_pawn))
finish_action(controller, FALSE)
return
var/list/emote_list = controller.blackboard[emote_key]
var/emote
if (islist(emote_list))
emote = length(emote_list) ? pick(emote_list) : null
else
emote = emote_list
if(isnull(emote))
finish_action(controller, FALSE)
return
living_pawn.emote(emote)
finish_action(controller, TRUE)

View File

@@ -228,6 +228,6 @@
emote_see = speech_lines[BB_EMOTE_SEE] || list()
emote_hear = speech_lines[BB_EMOTE_HEAR] || list()
sound = speech_lines[BB_EMOTE_SOUND] || list()
speech_chance = speech_lines[BB_EMOTE_CHANCE] ? speech_lines[BB_EMOTE_CHANCE] : initial(speech_chance)
speech_chance = speech_lines[BB_SPEAK_CHANCE] ? speech_lines[BB_SPEAK_CHANCE] : initial(speech_chance)
return ..()

View File

@@ -0,0 +1,50 @@
/**
* Basic handling for showing held items in a mob's hands
*/
/datum/component/basic_inhands
/// Layer index we show our inhands upon
var/display_layer
/// Y offset to apply to inhands
var/y_offset
/// X offset to apply to inhands, is inverted for the left hand
var/x_offset
/// What overlays are we currently showing?
var/list/cached_overlays
/datum/component/basic_inhands/Initialize(display_layer = 1, y_offset = 0, x_offset = 0)
. = ..()
if (!isliving(parent))
return COMPONENT_INCOMPATIBLE
src.display_layer = display_layer
src.y_offset = y_offset
src.x_offset = x_offset
cached_overlays = list()
/datum/component/basic_inhands/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_updated_overlays))
RegisterSignal(parent, COMSIG_MOB_UPDATE_HELD_ITEMS, PROC_REF(on_updated_held_items))
/datum/component/basic_inhands/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_MOB_UPDATE_HELD_ITEMS))
/// When your overlays update, add your held overlays
/datum/component/basic_inhands/proc/on_updated_overlays(atom/parent_atom, list/overlays)
SIGNAL_HANDLER
overlays += cached_overlays
/// When your number of held items changes, regenerate held icons
/datum/component/basic_inhands/proc/on_updated_held_items(mob/living/holding_mob)
SIGNAL_HANDLER
var/list/held_overlays = list()
for(var/obj/item/held in holding_mob.held_items)
var/is_right = holding_mob.get_held_index_of_item(held) % 2 == 0
var/icon_file = is_right ? held.righthand_file : held.lefthand_file
var/mutable_appearance/held_overlay = held.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE)
held_overlay.pixel_y += y_offset
held_overlay.pixel_x += x_offset * (is_right ? 1 : -1)
held_overlays += held_overlay
cached_overlays = held_overlays
holding_mob.update_appearance(UPDATE_OVERLAYS)

View File

@@ -68,7 +68,7 @@
/// Chop one off
/datum/element/amputating_limbs/proc/amputate(mob/living/surgeon, mob/living/carbon/victim, obj/item/bodypart/to_remove)
surgeon.visible_message(span_warning("[surgeon] begins [surgery_verb] [to_remove] off of [victim]!"))
surgeon.visible_message(span_warning("[surgeon] [surgery_verb] [to_remove] off of [victim]!"))
if (surgery_time > 0 && !do_after(surgeon, delay = surgery_time, target = victim))
return
to_remove.dismember()

View File

@@ -257,6 +257,7 @@
/mob/living/basic/cow/wisdom,
/mob/living/basic/crab,
/mob/living/basic/goat,
/mob/living/basic/gorilla,
/mob/living/basic/headslug,
/mob/living/basic/killer_tomato,
/mob/living/basic/lizard,
@@ -274,7 +275,6 @@
/mob/living/basic/statue,
/mob/living/basic/stickman,
/mob/living/basic/stickman/dog,
/mob/living/simple_animal/hostile/gorilla,
/mob/living/simple_animal/hostile/megafauna/dragon/lesser,
/mob/living/simple_animal/parrot,
/mob/living/simple_animal/pet/cat,

View File

@@ -148,7 +148,7 @@
show_in_report = FALSE // Selective attention test. Did you spot the gorilla?
/// The gorilla we created, we only hold this ref until the round starts.
var/mob/living/simple_animal/hostile/gorilla/cargo_domestic/cargorilla
var/mob/living/basic/gorilla/cargorilla/cargorilla
/datum/station_trait/cargorilla/New()
. = ..()
@@ -189,7 +189,7 @@
cargorilla = null
/// Get us a ghost for the gorilla.
/datum/station_trait/cargorilla/proc/get_ghost_for_gorilla(mob/living/simple_animal/hostile/gorilla/cargo_domestic/gorilla)
/datum/station_trait/cargorilla/proc/get_ghost_for_gorilla(mob/living/basic/gorilla/cargorilla/gorilla)
if(QDELETED(gorilla))
return

View File

@@ -62,7 +62,7 @@
/datum/reagent/medicine/strange_reagent = 5,
)
tastes = list("the jungle" = 1, "bananas" = 1, "jimmies" = 1)
spawned_mob = /mob/living/simple_animal/hostile/gorilla
spawned_mob = /mob/living/basic/gorilla
/obj/item/food/monkeycube/chicken
name = "chicken cube"

View File

@@ -26,9 +26,9 @@
JOB_CHIEF_MEDICAL_OFFICER = /mob/living/simple_animal/pet/cat/runtime,
JOB_CHIEF_ENGINEER = /mob/living/simple_animal/parrot/poly,
JOB_QUARTERMASTER = list(
/mob/living/basic/gorilla/cargorilla,
/mob/living/basic/sloth/citrus,
/mob/living/basic/sloth/paperwork,
/mob/living/simple_animal/hostile/gorilla/cargo_domestic,
)
)
/// The head that we are targetting

View File

@@ -286,6 +286,10 @@
I.dropped(src)
return FALSE
/// Returns true if a mob is holding something
/mob/proc/is_holding_items()
return !!locate(/obj/item) in held_items
/mob/proc/drop_all_held_items()
. = FALSE
for(var/obj/item/I in held_items)

View File

@@ -291,6 +291,7 @@
update_held_items()
/mob/living/basic/update_held_items()
. = ..()
if(isnull(client) || isnull(hud_used) || hud_used.hud_version == HUD_STYLE_NOHUD)
return
var/turf/our_turf = get_turf(src)

View File

@@ -37,7 +37,7 @@
BB_EMOTE_SAY = list("HONK", "Honk!", "Welcome to clown planet!"),
BB_EMOTE_HEAR = list("honks", "squeaks"),
BB_EMOTE_SOUND = list('sound/items/bikehorn.ogg'), //WE LOVE TO PARTY
BB_EMOTE_CHANCE = 5,
BB_SPEAK_CHANCE = 5,
)
///do we waddle (honk)
var/waddles = TRUE
@@ -150,9 +150,9 @@
),
BB_EMOTE_HEAR = list("honks", "contemplates its existence"),
BB_EMOTE_SEE = list("sweats", "jiggles"),
BB_EMOTE_CHANCE = 5,
BB_SPEAK_CHANCE = 5,
)
/mob/living/basic/clown/fleshclown/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
@@ -185,7 +185,7 @@
emotes = list(
BB_EMOTE_SAY = list("YA-HONK!!!"),
BB_EMOTE_HEAR = list("honks", "squeaks"),
BB_EMOTE_CHANCE = 60,
BB_SPEAK_CHANCE = 60,
)
/mob/living/basic/clown/clownhulk
@@ -221,7 +221,7 @@
BB_EMOTE_SAY = list("HONK", "Honk!", "HAUAUANK!!!", "GUUURRRRAAAHHH!!!"),
BB_EMOTE_HEAR = list("honks", "grunts"),
BB_EMOTE_SEE = list("sweats"),
BB_EMOTE_CHANCE = 5,
BB_SPEAK_CHANCE = 5,
)
/mob/living/basic/clown/clownhulk/chlown
@@ -252,7 +252,7 @@
emotes = list(
BB_EMOTE_SAY = list("HONK", "Honk!", "Bruh", "cheeaaaahhh?"),
BB_EMOTE_SEE = list("asserts his dominance", "emasculates everyone implicitly"),
BB_EMOTE_CHANCE = 5,
BB_SPEAK_CHANCE = 5,
)
/mob/living/basic/clown/clownhulk/honkmunculus
@@ -318,7 +318,7 @@
BB_EMOTE_SAY = list("HONK!!!", "The Honkmother is merciful, so I must act out her wrath.", "parce mihi ad beatus honkmother placet mihi ut peccata committere,", "DIE!!!"),
BB_EMOTE_HEAR = list("honks", "grunts"),
BB_EMOTE_SEE = list("sweats"),
BB_EMOTE_CHANCE = 5,
BB_SPEAK_CHANCE = 5,
)
/mob/living/basic/clown/mutant
@@ -354,7 +354,7 @@
emotes = list(
BB_EMOTE_SAY = list("aaaaaahhhhuuhhhuhhhaaaaa", "AAAaaauuuaaAAAaauuhhh", "huuuuuh... hhhhuuuooooonnnnkk", "HuaUAAAnKKKK"),
BB_EMOTE_SEE = list("squirms", "writhes", "pulsates", "froths", "oozes"),
BB_EMOTE_CHANCE = 10,
BB_SPEAK_CHANCE = 10,
)
/mob/living/basic/clown/mutant/slow

View File

@@ -0,0 +1,181 @@
/// Where do we draw gorilla held overlays?
#define GORILLA_HANDS_LAYER 1
/**
* Like a bigger monkey
* They make a lot of noise and punch limbs off unconscious folks
*/
/mob/living/basic/gorilla
name = "Gorilla"
desc = "A ground-dwelling, predominantly herbivorous ape which usually inhabits the forests of central Africa but today is quite far away from there."
icon = 'icons/mob/simple/gorilla.dmi'
icon_state = "crawling"
icon_living = "crawling"
icon_dead = "dead"
health_doll_icon = "crawling"
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
maxHealth = 220
health = 220
response_help_continuous = "prods"
response_help_simple = "prod"
response_disarm_continuous = "challenges"
response_disarm_simple = "challenge"
response_harm_continuous = "thumps"
response_harm_simple = "thump"
speed = 0.5
melee_damage_lower = 15
melee_damage_upper = 18
damage_coeff = list(BRUTE = 1, BURN = 1.5, TOX = 1.5, CLONE = 0, STAMINA = 0, OXY = 1.5)
obj_damage = 20
attack_verb_continuous = "pummels"
attack_verb_simple = "pummel"
attack_sound = 'sound/weapons/punch1.ogg'
unique_name = TRUE
ai_controller = /datum/ai_controller/basic_controller/gorilla
faction = list(FACTION_MONKEY, FACTION_JUNGLE)
butcher_results = list(/obj/item/food/meat/slab/gorilla = 4, /obj/effect/gibspawner/generic/animal = 1)
/// How likely our meaty fist is to stun someone
var/paralyze_chance = 20
/// A counter for when we can scream again
var/oogas = 0
/// Types of things we want to find and eat
var/static/list/gorilla_food = list(
/obj/item/food/bread/banana,
/obj/item/food/breadslice/banana,
/obj/item/food/cnds/banana_honk,
/obj/item/food/grown/banana,
/obj/item/food/popsicle/topsicle/banana,
/obj/item/food/salad/fruit,
/obj/item/food/salad/jungle,
/obj/item/food/sundae,
)
/mob/living/basic/gorilla/Initialize(mapload)
. = ..()
add_traits(list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP), ROUNDSTART_TRAIT)
AddElement(/datum/element/wall_smasher)
AddElement(/datum/element/dextrous)
AddElement(/datum/element/footstep, FOOTSTEP_MOB_BAREFOOT)
AddElement(/datum/element/basic_eating, heal_amt = 10, food_types = gorilla_food)
AddElement(
/datum/element/amputating_limbs, \
surgery_time = 0 SECONDS, \
surgery_verb = "punches",\
)
AddComponent(/datum/component/personal_crafting)
AddComponent(/datum/component/basic_inhands, y_offset = -1)
ai_controller?.set_blackboard_key(BB_BASIC_FOODS, gorilla_food)
/mob/living/basic/gorilla/update_overlays()
. = ..()
if (is_holding_items())
. += "standing_overlay"
/mob/living/basic/gorilla/update_icon_state()
. = ..()
if (stat == DEAD)
return
icon_state = is_holding_items() ? "standing" : "crawling"
/mob/living/basic/gorilla/update_held_items()
. = ..()
update_appearance(UPDATE_ICON)
if (is_holding_items())
add_movespeed_modifier(/datum/movespeed_modifier/gorilla_standing)
else
remove_movespeed_modifier(/datum/movespeed_modifier/gorilla_standing)
/mob/living/basic/gorilla/melee_attack(mob/living/target, list/modifiers, ignore_cooldown)
. = ..()
if (!. || !isliving(target))
return
ooga_ooga()
if (prob(paralyze_chance))
target.Paralyze(2 SECONDS)
visible_message(span_danger("[src] knocks [target] down!"))
else
target.throw_at(get_edge_target_turf(target, dir), range = rand(1, 2), speed = 7, thrower = src)
/mob/living/basic/gorilla/gib(drop_bitflags = DROP_BRAIN)
if(!(drop_bitflags & DROP_BRAIN))
return ..()
var/mob/living/brain/gorilla_brain = new(drop_location())
gorilla_brain.name = real_name
gorilla_brain.real_name = real_name
mind?.transfer_to(gorilla_brain)
return ..()
/mob/living/basic/gorilla/can_use_guns(obj/item/gun)
to_chat(src, span_warning("Your meaty finger is much too large for the trigger guard!"))
return FALSE
/// Assert your dominance with audio cues
/mob/living/basic/gorilla/proc/ooga_ooga()
if (isnull(client))
return // Sorry NPCs
oogas -= 1
if(oogas > 0)
return
oogas = rand(2,6)
emote("ooga")
/// Gorillas are slower when carrying something
/datum/movespeed_modifier/gorilla_standing
blacklisted_movetypes = (FLYING|FLOATING)
multiplicative_slowdown = 0.5
/// A smaller gorilla summoned via magic
/mob/living/basic/gorilla/lesser
name = "lesser Gorilla"
desc = "An adolescent Gorilla. It may not be fully grown but, much like a banana, that just means it's sturdier and harder to chew!"
maxHealth = 120
health = 120
speed = 0.35
melee_damage_lower = 10
melee_damage_upper = 15
obj_damage = 15
ai_controller = /datum/ai_controller/basic_controller/gorilla/lesser
butcher_results = list(/obj/item/food/meat/slab/gorilla = 2)
/mob/living/basic/gorilla/lesser/Initialize(mapload)
. = ..()
transform *= 0.75
/// Cargo's wonderful mascot, the tranquil box-carrying ape
/mob/living/basic/gorilla/cargorilla
name = "Cargorilla" // Overriden, normally
icon = 'icons/mob/simple/cargorillia.dmi'
desc = "Cargo's pet gorilla. They seem to have an 'I love Mom' tattoo."
maxHealth = 200
health = 200
faction = list(FACTION_NEUTRAL, FACTION_MONKEY, FACTION_JUNGLE)
unique_name = FALSE
ai_controller = null
/mob/living/basic/gorilla/cargorilla/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_PACIFISM, INNATE_TRAIT)
AddComponent(/datum/component/crate_carrier)
/**
* Poll ghosts for control of the gorilla. Not added in init because we only want to poll when the round starts.
* Preferably in future we can replace this with a popup on the lobby to queue to become a gorilla.
*/
/mob/living/basic/gorilla/cargorilla/proc/poll_for_gorilla()
AddComponent(\
/datum/component/ghost_direct_control,\
poll_candidates = TRUE,\
poll_length = 30 SECONDS,\
role_name = "Cargorilla",\
assumed_control_message = "You are Cargorilla, a pacifist friend of the station and carrier of freight.",\
poll_ignore_key = POLL_IGNORE_CARGORILLA,\
after_assumed_control = CALLBACK(src, PROC_REF(became_player_controlled)),\
)
/// Called once a ghost assumes control
/mob/living/basic/gorilla/cargorilla/proc/became_player_controlled()
mind.set_assigned_role(SSjob.GetJobType(/datum/job/cargo_technician))
mind.special_role = "Cargorilla"
to_chat(src, span_notice("You can pick up crates by clicking on them, and drop them by clicking on the ground."))
#undef GORILLA_HANDS_LAYER

View File

@@ -0,0 +1,5 @@
/// Cargorilla's ID card
/obj/item/card/id/advanced/cargo_gorilla
name = "cargorilla ID"
desc = "A card used to provide ID and determine access across the station. A gorilla-sized ID for a gorilla-sized cargo technician."
trim = /datum/id_trim/job/cargo_technician

View File

@@ -0,0 +1,35 @@
/// Pretty basic, just click people to death. Also hunt and eat bananas.
/datum/ai_controller/basic_controller/gorilla
blackboard = list(
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/allow_items/gorilla,
BB_EMOTE_KEY = "ooga",
BB_EMOTE_CHANCE = 40,
)
ai_traits = STOP_MOVING_WHEN_PULLED
ai_movement = /datum/ai_movement/basic_avoidance
idle_behavior = /datum/idle_behavior/idle_random_walk
planning_subtrees = list(
/datum/ai_planning_subtree/run_emote,
/datum/ai_planning_subtree/find_food,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/attack_obstacle_in_path/gorilla,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
)
/datum/targetting_datum/basic/allow_items/gorilla
stat_attack = UNCONSCIOUS
/datum/ai_planning_subtree/attack_obstacle_in_path/gorilla
attack_behaviour = /datum/ai_behavior/attack_obstructions/gorilla
/datum/ai_behavior/attack_obstructions/gorilla
can_attack_turfs = TRUE
/datum/ai_controller/basic_controller/gorilla/lesser
blackboard = list(
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/allow_items,
BB_EMOTE_KEY = "ooga",
BB_EMOTE_CHANCE = 60,
)

View File

@@ -1,5 +1,5 @@
/datum/emote/gorilla
mob_type_allowed_typecache = /mob/living/simple_animal/hostile/gorilla
mob_type_allowed_typecache = /mob/living/basic/gorilla
mob_type_blacklist_typecache = list()
/datum/emote/gorilla/ooga
@@ -9,4 +9,3 @@
message_param = "oogas at %t."
emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
sound = 'sound/creatures/gorilla.ogg'

View File

@@ -38,7 +38,7 @@
AddElement(\
/datum/element/amputating_limbs,\
surgery_time = 0 SECONDS,\
surgery_verb = "tearing",\
surgery_verb = "tears",\
minimum_stat = CONSCIOUS,\
snip_chance = 10,\
target_zones = GLOB.arm_zones,\

View File

@@ -39,7 +39,7 @@
AddElement(/datum/element/basic_eating, food_types = target_foods)
AddElement(\
/datum/element/amputating_limbs,\
surgery_verb = "snipping",\
surgery_verb = "begins snipping",\
target_zones = GLOB.arm_zones,\
)
charge = new(src)

View File

@@ -76,7 +76,7 @@
//Royals have bigger sprites, so inhand things must be handled differently.
/mob/living/carbon/alien/adult/royal/update_held_items()
..()
. = ..()
remove_overlay(HANDS_LAYER)
var/list/hands = list()

View File

@@ -282,11 +282,17 @@
update_body()
/mob/living/carbon/update_held_items()
. = ..()
remove_overlay(HANDS_LAYER)
if (handcuffed)
drop_all_held_items()
return
overlays_standing[HANDS_LAYER] = get_held_overlays()
apply_overlay(HANDS_LAYER)
/// Generate held item overlays
/mob/living/carbon/proc/get_held_overlays()
var/list/hands = list()
for(var/obj/item/I in held_items)
if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
@@ -307,9 +313,7 @@
icon_file = I.righthand_file
hands += I.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE)
overlays_standing[HANDS_LAYER] = hands
apply_overlay(HANDS_LAYER)
return hands
/mob/living/carbon/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "")
var/fire_icon = "[dna?.species.fire_overlay || "human"]_[stacks > MOB_BIG_FIRE_STACK_THRESHOLD ? "big_fire" : "small_fire"][suffix]"

View File

@@ -481,12 +481,7 @@ There are several things that need to be remembered:
apply_overlay(LEGCUFF_LAYER)
throw_alert("legcuffed", /atom/movable/screen/alert/restrained/legcuffed, new_master = src.legcuffed)
/mob/living/carbon/human/update_held_items()
remove_overlay(HANDS_LAYER)
if (handcuffed)
drop_all_held_items()
return
/mob/living/carbon/human/get_held_overlays()
var/list/hands = list()
for(var/obj/item/worn_item in held_items)
var/held_index = get_held_index_of_item(worn_item)
@@ -515,8 +510,7 @@ There are several things that need to be remembered:
held_in_hand?.held_hand_offset?.apply_offset(hand_overlay)
hands += hand_overlay
overlays_standing[HANDS_LAYER] = hands
apply_overlay(HANDS_LAYER)
return hands
/proc/wear_female_version(t_color, icon, layer, type, greyscale_colors)
var/index = "[t_color]-[greyscale_colors]"

View File

@@ -1414,6 +1414,7 @@
/mob/living/basic/cow,
/mob/living/basic/crab,
/mob/living/basic/goat,
/mob/living/basic/gorilla,
/mob/living/basic/headslug,
/mob/living/basic/killer_tomato,
/mob/living/basic/lizard,
@@ -1431,7 +1432,6 @@
/mob/living/basic/statue,
/mob/living/basic/stickman,
/mob/living/basic/stickman/dog,
/mob/living/simple_animal/hostile/gorilla,
/mob/living/simple_animal/hostile/megafauna/dragon/lesser,
/mob/living/simple_animal/parrot,
/mob/living/simple_animal/pet/cat,

View File

@@ -173,6 +173,7 @@
. = ..()
GLOB.drones_list += src
access_card = new /obj/item/card/id/advanced/simple_bot(src)
AddComponent(/datum/component/basic_inhands, y_offset = getItemPixelShiftY())
// Doing this hurts my soul, but simple_animal access reworks are for another day.
var/datum/id_trim/job/cap_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/captain]

View File

@@ -27,45 +27,6 @@
if(slot_flags & (ITEM_SLOT_HANDS|ITEM_SLOT_BACKPACK|ITEM_SLOT_DEX_STORAGE))
update_inv_internal_storage()
/mob/living/simple_animal/drone/update_held_items()
remove_overlay(DRONE_HANDS_LAYER)
var/list/hands_overlays = list()
var/obj/item/l_hand = get_item_for_held_index(1)
var/obj/item/r_hand = get_item_for_held_index(2)
var/y_shift = getItemPixelShiftY()
if(r_hand)
var/mutable_appearance/r_hand_overlay = r_hand.build_worn_icon(default_layer = DRONE_HANDS_LAYER, default_icon_file = r_hand.righthand_file, isinhands = TRUE)
if(y_shift)
r_hand_overlay.pixel_y += y_shift
hands_overlays += r_hand_overlay
if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
SET_PLANE_EXPLICIT(r_hand, ABOVE_HUD_PLANE, src)
r_hand.screen_loc = ui_hand_position(get_held_index_of_item(r_hand))
client.screen |= r_hand
if(l_hand)
var/mutable_appearance/l_hand_overlay = l_hand.build_worn_icon(default_layer = DRONE_HANDS_LAYER, default_icon_file = l_hand.lefthand_file, isinhands = TRUE)
if(y_shift)
l_hand_overlay.pixel_y += y_shift
hands_overlays += l_hand_overlay
if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
SET_PLANE_EXPLICIT(l_hand, ABOVE_HUD_PLANE, src)
l_hand.screen_loc = ui_hand_position(get_held_index_of_item(l_hand))
client.screen |= l_hand
if(hands_overlays.len)
drone_overlays[DRONE_HANDS_LAYER] = hands_overlays
apply_overlay(DRONE_HANDS_LAYER)
/mob/living/simple_animal/drone/proc/update_inv_internal_storage()
if(internal_storage && client && hud_used?.hud_shown)
internal_storage.screen_loc = ui_drone_storage

View File

@@ -90,6 +90,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
GLOB.parasites += src
update_theme(theme)
AddElement(/datum/element/simple_flying)
AddComponent(/datum/component/basic_inhands)
manifest_effects()
/mob/living/simple_animal/hostile/guardian/Destroy() //if deleted by admins or something random, cut from the summoner
@@ -462,32 +463,6 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
cut_overlay(overlay)
guardian_overlays[cache_index] = null
/mob/living/simple_animal/hostile/guardian/update_held_items()
remove_overlay(GUARDIAN_HANDS_LAYER)
var/list/hands_overlays = list()
var/obj/item/l_hand = get_item_for_held_index(1)
var/obj/item/r_hand = get_item_for_held_index(2)
if(r_hand)
hands_overlays += r_hand.build_worn_icon(default_layer = GUARDIAN_HANDS_LAYER, default_icon_file = r_hand.righthand_file, isinhands = TRUE)
if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
SET_PLANE_EXPLICIT(r_hand, ABOVE_HUD_PLANE, src)
r_hand.screen_loc = ui_hand_position(get_held_index_of_item(r_hand))
client.screen |= r_hand
if(l_hand)
hands_overlays += l_hand.build_worn_icon(default_layer = GUARDIAN_HANDS_LAYER, default_icon_file = l_hand.lefthand_file, isinhands = TRUE)
if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
SET_PLANE_EXPLICIT(l_hand, ABOVE_HUD_PLANE, src)
l_hand.screen_loc = ui_hand_position(get_held_index_of_item(l_hand))
client.screen |= l_hand
if(length(hands_overlays))
guardian_overlays[GUARDIAN_HANDS_LAYER] = hands_overlays
apply_overlay(GUARDIAN_HANDS_LAYER)
/mob/living/simple_animal/hostile/guardian/regenerate_icons()
update_held_items()

View File

@@ -1,174 +0,0 @@
#define GORILLA_TOTAL_LAYERS 1
/mob/living/simple_animal/hostile/gorilla
name = "Gorilla"
desc = "A ground-dwelling, predominantly herbivorous ape that inhabits the forests of central Africa."
icon = 'icons/mob/simple/gorilla.dmi'
icon_state = "crawling"
icon_living = "crawling"
icon_dead = "dead"
health_doll_icon = "crawling"
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
speak_chance = 80
maxHealth = 220
health = 220
loot = list(/obj/effect/gibspawner/generic/animal)
butcher_results = list(/obj/item/food/meat/slab/gorilla = 4)
response_help_continuous = "prods"
response_help_simple = "prod"
response_disarm_continuous = "challenges"
response_disarm_simple = "challenge"
response_harm_continuous = "thumps"
response_harm_simple = "thump"
speed = 0.5
melee_damage_lower = 15
melee_damage_upper = 18
damage_coeff = list(BRUTE = 1, BURN = 1.5, TOX = 1.5, CLONE = 0, STAMINA = 0, OXY = 1.5)
obj_damage = 20
environment_smash = ENVIRONMENT_SMASH_WALLS
attack_verb_continuous = "pummels"
attack_verb_simple = "pummel"
attack_sound = 'sound/weapons/punch1.ogg'
dextrous = TRUE
hud_type = /datum/hud/dextrous
held_items = list(null, null)
faction = list(FACTION_MONKEY, FACTION_JUNGLE)
robust_searching = TRUE
stat_attack = HARD_CRIT
minbodytemp = 270
maxbodytemp = 350
unique_name = TRUE
footstep_type = FOOTSTEP_MOB_BAREFOOT
var/list/gorilla_overlays[GORILLA_TOTAL_LAYERS]
var/oogas = 0
// Gorillas like to dismember limbs from unconscious mobs.
// Returns null when the target is not an unconscious carbon mob; a list of limbs (possibly empty) otherwise.
/mob/living/simple_animal/hostile/gorilla/proc/get_target_bodyparts(atom/hit_target)
if(!iscarbon(hit_target))
return
var/mob/living/carbon/carbon_target = hit_target
if(carbon_target.stat < UNCONSCIOUS)
return
var/list/parts = list()
for(var/obj/item/bodypart/part as anything in carbon_target.bodyparts)
if(part.body_part == HEAD || part.body_part == CHEST)
continue
if(part.bodypart_flags & BODYPART_UNREMOVABLE)
continue
parts += part
return parts
/mob/living/simple_animal/hostile/gorilla/AttackingTarget(atom/attacked_target)
. = ..()
if(!.)
return
if(client)
oogaooga()
var/list/parts = get_target_bodyparts(target)
if(length(parts))
var/obj/item/bodypart/to_dismember = pick(parts)
to_dismember.dismember()
return
if(isliving(target))
var/mob/living/living_target = target
if(prob(80))
living_target.throw_at(get_edge_target_turf(living_target, dir), rand(1, 2), 7, src)
else
living_target.Paralyze(2 SECONDS)
visible_message(span_danger("[src] knocks [living_target] down!"))
/mob/living/simple_animal/hostile/gorilla/CanAttack(atom/the_target)
var/list/parts = get_target_bodyparts(target)
return ..() && !ismonkey(the_target) && (!parts || length(parts) > 3)
/mob/living/simple_animal/hostile/gorilla/CanSmashTurfs(turf/T)
return iswallturf(T)
/mob/living/simple_animal/hostile/gorilla/gib(drop_bitflags=DROP_BRAIN)
if(drop_bitflags & DROP_BRAIN)
var/mob/living/brain/gorilla_brain = new(drop_location())
gorilla_brain.name = real_name
gorilla_brain.real_name = real_name
mind?.transfer_to(gorilla_brain)
return ..()
/mob/living/simple_animal/hostile/gorilla/handle_automated_speech(override)
if(speak_chance && (override || prob(speak_chance)))
playsound(src, 'sound/creatures/gorilla.ogg', 50)
return ..()
/mob/living/simple_animal/hostile/gorilla/can_use_guns(obj/item/G)
to_chat(src, span_warning("Your meaty finger is much too large for the trigger guard!"))
return FALSE
/mob/living/simple_animal/hostile/gorilla/proc/oogaooga()
oogas -= 1
if(oogas <= 0)
oogas = rand(2,6)
playsound(src, 'sound/creatures/gorilla.ogg', 50)
/mob/living/simple_animal/hostile/gorilla/lesser
name = "lesser Gorilla"
desc = "An adolescent Gorilla. It may not be fully grown but, much like a banana, that just means it's sturdier and harder to chew!"
speak_chance = 100 // compensating for something
maxHealth = 120
health = 120
butcher_results = list(/obj/item/food/meat/slab/gorilla = 2)
speed = 0.35
melee_damage_lower = 10
melee_damage_upper = 15
obj_damage = 15
stat_attack = SOFT_CRIT
unique_name = TRUE
/mob/living/simple_animal/hostile/gorilla/lesser/Initialize(mapload)
. = ..()
transform *= 0.75 // smolrilla
/mob/living/simple_animal/hostile/gorilla/cargo_domestic
name = "Cargorilla" // Overriden, normally
icon = 'icons/mob/simple/cargorillia.dmi'
desc = "Cargo's pet gorilla. They seem to have an 'I love Mom' tattoo."
maxHealth = 200
health = 200
faction = list(FACTION_NEUTRAL, FACTION_MONKEY, FACTION_JUNGLE)
gold_core_spawnable = NO_SPAWN
unique_name = FALSE
/mob/living/simple_animal/hostile/gorilla/cargo_domestic/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_PACIFISM, INNATE_TRAIT)
AddComponent(/datum/component/crate_carrier)
/// Poll ghosts for control of the gorilla.
/mob/living/simple_animal/hostile/gorilla/cargo_domestic/proc/poll_for_gorilla()
AddComponent(\
/datum/component/ghost_direct_control,\
poll_candidates = TRUE,\
poll_length = 30 SECONDS,\
role_name = "Cargorilla",\
assumed_control_message = "You are Cargorilla, a pacifistic friend of the station and carrier of freight.",\
poll_ignore_key = POLL_IGNORE_CARGORILLA,\
after_assumed_control = CALLBACK(src, PROC_REF(became_player_controlled)),\
)
/// Called once a ghost assumes control
/mob/living/simple_animal/hostile/gorilla/cargo_domestic/proc/became_player_controlled()
mind.set_assigned_role(SSjob.GetJobType(/datum/job/cargo_technician))
mind.special_role = "Cargorilla"
to_chat(src, span_notice("You can pick up crates by clicking on them, and drop them by clicking on the ground."))
/obj/item/card/id/advanced/cargo_gorilla
name = "cargorilla ID"
desc = "A card used to provide ID and determine access across the station. A gorilla-sized ID for a gorilla-sized cargo technician."
trim = /datum/id_trim/job/cargo_technician
#undef GORILLA_TOTAL_LAYERS

View File

@@ -1,56 +0,0 @@
#define GORILLA_HANDS_LAYER 1
/mob/living/simple_animal/hostile/gorilla/proc/apply_overlay(cache_index)
. = gorilla_overlays[cache_index]
if(.)
add_overlay(.)
/mob/living/simple_animal/hostile/gorilla/proc/remove_overlay(cache_index)
var/I = gorilla_overlays[cache_index]
if(I)
cut_overlay(I)
gorilla_overlays[cache_index] = null
/mob/living/simple_animal/hostile/gorilla/update_held_items()
cut_overlays("standing_overlay")
remove_overlay(GORILLA_HANDS_LAYER)
var/standing = FALSE
for(var/I in held_items)
if(I)
standing = TRUE
break
if(!standing)
if(stat != DEAD)
icon_state = "crawling"
set_varspeed(0.5)
return ..()
if(stat != DEAD)
icon_state = "standing"
set_varspeed(1) // Gorillas are slow when standing up.
var/list/hands_overlays = list()
var/obj/item/l_hand = get_item_for_held_index(1)
var/obj/item/r_hand = get_item_for_held_index(2)
if(r_hand)
var/mutable_appearance/r_hand_overlay = r_hand.build_worn_icon(default_layer = GORILLA_HANDS_LAYER, default_icon_file = r_hand.righthand_file, isinhands = TRUE)
r_hand_overlay.pixel_y -= 1
hands_overlays += r_hand_overlay
if(l_hand)
var/mutable_appearance/l_hand_overlay = l_hand.build_worn_icon(default_layer = GORILLA_HANDS_LAYER, default_icon_file = l_hand.lefthand_file, isinhands = TRUE)
l_hand_overlay.pixel_y -= 1
hands_overlays += l_hand_overlay
if(hands_overlays.len)
gorilla_overlays[GORILLA_HANDS_LAYER] = hands_overlays
apply_overlay(GORILLA_HANDS_LAYER)
add_overlay("standing_overlay")
return ..()
/mob/living/simple_animal/hostile/gorilla/regenerate_icons()
update_held_items()
#undef GORILLA_HANDS_LAYER

View File

@@ -563,6 +563,7 @@
update_held_items()
/mob/living/simple_animal/update_held_items()
. = ..()
if(!client || !hud_used || hud_used.hud_version == HUD_STYLE_NOHUD)
return
var/turf/our_turf = get_turf(src)

View File

@@ -26,7 +26,8 @@
///Updates the held items overlay(s) & HUD element.
/mob/proc/update_held_items()
return
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_MOB_UPDATE_HELD_ITEMS)
///Updates the mask overlay & HUD element.
/mob/proc/update_worn_mask()

View File

@@ -298,7 +298,7 @@
regenerate_icons()
icon = null
invisibility = INVISIBILITY_MAXIMUM
var/mob/living/simple_animal/hostile/gorilla/new_gorilla = new (get_turf(src))
var/mob/living/basic/gorilla/new_gorilla = new (get_turf(src))
new_gorilla.set_combat_mode(TRUE)
if(mind)
mind.transfer_to(new_gorilla)

View File

@@ -14,14 +14,18 @@
invocation_type = INVOCATION_SHOUT
summon_radius = 2
summon_type = list(/mob/living/carbon/human/species/monkey/angry, /mob/living/carbon/human/species/monkey/angry, /mob/living/simple_animal/hostile/gorilla/lesser)
summon_type = list(
/mob/living/basic/gorilla/lesser,
/mob/living/carbon/human/species/monkey/angry,
/mob/living/carbon/human/species/monkey/angry, // Listed twice so it's twice as likely, this class doesn't use pick weight
)
summon_amount = 4
/datum/action/cooldown/spell/conjure/simian/level_spell(bypass_cap)
. = ..()
summon_amount++ // MORE, MOOOOORE
if(spell_level == spell_max_level) // We reward the faithful.
summon_type = list(/mob/living/carbon/human/species/monkey/angry, /mob/living/simple_animal/hostile/gorilla)
summon_type = list(/mob/living/carbon/human/species/monkey/angry, /mob/living/basic/gorilla)
spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC // Max level lets you cast it naked, for monkey larp.
to_chat(owner, span_notice("Your simian power has reached maximum capacity! You can now cast this spell naked, and you will create adult Gorillas with each cast."))

View File

@@ -86,9 +86,6 @@
/mob/living/simple_animal/hostile/construct/wraith/mystic,
/mob/living/simple_animal/hostile/construct/wraith/noncult,
/mob/living/simple_animal/hostile/dark_wizard,
/mob/living/simple_animal/hostile/gorilla,
/mob/living/simple_animal/hostile/gorilla/lesser,
/mob/living/simple_animal/hostile/gorilla/cargo_domestic,
/mob/living/simple_animal/hostile/guardian,
/mob/living/simple_animal/hostile/guardian/assassin,
/mob/living/simple_animal/hostile/guardian/charger,

View File

@@ -37,3 +37,4 @@ To defeat the slaughter demon, shoot at it until it dies.
When a round ends nearly everything about it is lost forever, leave your salt behind with it.
You can win a pulse rifle from the arcade machine. Honest.
Your sprite represents your hitbox, so that afro makes you easier to kill. The sacrifices we make for style.
Gorillas can be killed by land mines placed along forest paths.

View File

@@ -828,6 +828,7 @@
#include "code\datums\ai\basic_mobs\basic_subtrees\move_to_cardinal.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\opportunistic_ventcrawler.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\ranged_skirmish.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\run_emote.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\shapechange_ambush.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\simple_attack_target.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_nearest_target_to_flee.dm"
@@ -936,6 +937,7 @@
#include "code\datums\components\attached_sticker.dm"
#include "code\datums\components\aura_healing.dm"
#include "code\datums\components\bakeable.dm"
#include "code\datums\components\basic_inhands.dm"
#include "code\datums\components\basic_mob_attack_telegraph.dm"
#include "code\datums\components\basic_ranged_ready_overlay.dm"
#include "code\datums\components\beetlejuice.dm"
@@ -4381,6 +4383,10 @@
#include "code\modules\mob\living\basic\farm_animals\goat\_goat.dm"
#include "code\modules\mob\living\basic\farm_animals\goat\goat_ai.dm"
#include "code\modules\mob\living\basic\farm_animals\goat\goat_subtypes.dm"
#include "code\modules\mob\living\basic\farm_animals\gorilla\gorilla.dm"
#include "code\modules\mob\living\basic\farm_animals\gorilla\gorilla_accessories.dm"
#include "code\modules\mob\living\basic\farm_animals\gorilla\gorilla_ai.dm"
#include "code\modules\mob\living\basic\farm_animals\gorilla\gorilla_emotes.dm"
#include "code\modules\mob\living\basic\heretic\ash_spirit.dm"
#include "code\modules\mob\living\basic\heretic\fire_shark.dm"
#include "code\modules\mob\living\basic\heretic\flesh_stalker.dm"
@@ -4747,9 +4753,6 @@
#include "code\modules\mob\living\simple_animal\hostile\constructs\constructs.dm"
#include "code\modules\mob\living\simple_animal\hostile\constructs\juggernaut.dm"
#include "code\modules\mob\living\simple_animal\hostile\constructs\wraith.dm"
#include "code\modules\mob\living\simple_animal\hostile\gorilla\emotes.dm"
#include "code\modules\mob\living\simple_animal\hostile\gorilla\gorilla.dm"
#include "code\modules\mob\living\simple_animal\hostile\gorilla\visuals_icons.dm"
#include "code\modules\mob\living\simple_animal\hostile\jungle\_jungle_mobs.dm"
#include "code\modules\mob\living\simple_animal\hostile\jungle\leaper.dm"
#include "code\modules\mob\living\simple_animal\hostile\megafauna\_megafauna.dm"

View File

@@ -0,0 +1,2 @@
/mob/living/simple_animal/hostile/gorilla/cargo_domestic : /mob/living/basic/gorilla/cargorilla{@OLD}
/mob/living/simple_animal/hostile/gorilla : /mob/living/basic/gorilla/@SUBTYPES{@OLD}