mirror of
https://github.com/Skyrat-SS13/Skyrat-tg.git
synced 2026-08-28 07:17:46 +01:00
* You can now raise lobstrosities from chasms chrabs. (#84969) ## About The Pull Request Lobstrosities can now be raised from aquarium icemoon/lavaland chrabs. First of all, you've to get a live chrab, an aquarium, and some fish feed. Second, you place the chrab inside the aquarium and turn the 'allow breeding' settting on (should probably rename it to a more apt name now). Keep the chrab well fed, and possibly with some friends and props in the same aquarium until it develops into a hopefully calm juveline lobstrosity and plops out of the aquarium (it can take some time). From there you can tame it by feeding it its favorite food: arms and lavaloop fish, and wait another dozen minutes for it to mature into a fully grown lobstrosity. Juveline lobstrosities are basically smaller and weaker lobstrosities, if not slightly faster in some ways. Unlike their taller counterparts, they can be tamed. Once done so, they'll retain their tamedness once grown up. Regardless, tamed lobstrosities can be given the pet command to fish for things by pointing at them. Thanks BenMatrix for the profound fisher component, woo. The chrab's weigth and size influence the growth speed of the first stage faster, meaning bigger chrabs (may require crossbreeding) will turn into juveline lobstrosities quickly. Amongst other things influencing the resulting mob are fish traits: Several traits have been given effects that apply to the mob, such as nocturnal regeneration, being venomous or being able to fly akin space carps. Also a new one that prevents the resulting lobstrosity from fully developing Now tested. ## Why It's Good For The Game I'm building upon fishing and aquarium stuff, which has been an interest of mine in a good while, though most of it doesn't have that many practical uses, I'm slowly trying to make it cooler, and chasm chrabs growing into lobstrosities is pretty much in line with the fluff texts for the fish. Eventually I'll have to add tips inside fishing toolboxes, otherwise people won't know even half of it. ## Changelog 🆑 add: You can raise lobstrosities from chasm chrabs inside an aquarium with the 'allow breeding' setting on. Keep the fish well fed, healthy and not lonely if you don't want an hostile one. add: Juveline lobstrosities (from chasms, plasma rivers, or aquariums, xenobio too) can be tamed with arms and lavaloop fishes. add: For lobstrosities grown from aquariums, they can have additional effects based on the fish traits they had in the aquarium, like being venomous or even flying. /🆑 * You can now raise lobstrosities from chasms chrabs. --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
324 lines
13 KiB
Plaintext
324 lines
13 KiB
Plaintext
/datum/action/cooldown/mob_cooldown/charge
|
|
name = "Charge"
|
|
button_icon = 'icons/mob/actions/actions_items.dmi'
|
|
button_icon_state = "sniper_zoom"
|
|
desc = "Allows you to charge at a chosen position."
|
|
cooldown_time = 1.5 SECONDS
|
|
/// Delay before the charge actually occurs
|
|
var/charge_delay = 0.3 SECONDS
|
|
/// The amount of turfs we move past the target
|
|
var/charge_past = 2
|
|
/// The maximum distance we can charge
|
|
var/charge_distance = 50
|
|
/// The sleep time before moving in deciseconds while charging
|
|
var/charge_speed = 0.5
|
|
/// The damage the charger does when bumping into something
|
|
var/charge_damage = 30
|
|
/// If we destroy objects while charging
|
|
var/destroy_objects = TRUE
|
|
/// If the current move is being triggered by us or not
|
|
var/actively_moving = FALSE
|
|
/// List of charging mobs
|
|
var/list/charging = list()
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/Activate(atom/target_atom)
|
|
disable_cooldown_actions()
|
|
// No charging and meleeing (overridded by StartCooldown after charge ends)
|
|
next_melee_use_time = world.time + 100 SECONDS
|
|
charge_sequence(owner, target_atom, charge_delay, charge_past)
|
|
StartCooldown()
|
|
enable_cooldown_actions()
|
|
return TRUE
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/charge_sequence(atom/movable/charger, atom/target_atom, delay, past)
|
|
do_charge(owner, target_atom, charge_delay, charge_past)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/do_charge(atom/movable/charger, atom/target_atom, delay, past)
|
|
if(!target_atom || target_atom == owner)
|
|
return
|
|
var/chargeturf = get_turf(target_atom)
|
|
if(!chargeturf)
|
|
return
|
|
var/dir = get_dir(charger, target_atom)
|
|
var/turf/target = get_ranged_target_turf(chargeturf, dir, past)
|
|
if(!target)
|
|
return
|
|
|
|
if(charger in charging)
|
|
// Stop any existing charging, this'll clean things up properly
|
|
GLOB.move_manager.stop_looping(charger)
|
|
|
|
charging += charger
|
|
actively_moving = FALSE
|
|
SEND_SIGNAL(owner, COMSIG_STARTED_CHARGE)
|
|
RegisterSignal(charger, COMSIG_MOVABLE_BUMP, PROC_REF(on_bump), override = TRUE)
|
|
RegisterSignal(charger, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_move), override = TRUE)
|
|
RegisterSignal(charger, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved), override = TRUE)
|
|
RegisterSignal(charger, COMSIG_LIVING_DEATH, PROC_REF(charge_end), override = TRUE)
|
|
charger.setDir(dir)
|
|
do_charge_indicator(charger, target)
|
|
|
|
SLEEP_CHECK_DEATH(delay, charger)
|
|
|
|
var/time_to_hit = min(get_dist(charger, target), charge_distance) * charge_speed
|
|
|
|
var/datum/move_loop/new_loop = GLOB.move_manager.home_onto(charger, target, delay = charge_speed, timeout = time_to_hit, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
|
|
if(!new_loop)
|
|
return
|
|
RegisterSignal(new_loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(pre_move), override = TRUE)
|
|
RegisterSignal(new_loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_move), override = TRUE)
|
|
RegisterSignal(new_loop, COMSIG_QDELETING, PROC_REF(charge_end), override = TRUE)
|
|
|
|
// Yes this is disgusting. But we need to queue this stuff, and this code just isn't setup to support that right now. So gotta do it with sleeps
|
|
sleep(time_to_hit + charge_speed)
|
|
charger.setDir(dir)
|
|
|
|
return TRUE
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/pre_move(datum)
|
|
SIGNAL_HANDLER
|
|
// If you sleep in Move() you deserve what's coming to you
|
|
actively_moving = TRUE
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/post_move(datum)
|
|
SIGNAL_HANDLER
|
|
actively_moving = FALSE
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/charge_end(datum/source)
|
|
SIGNAL_HANDLER
|
|
var/atom/movable/charger = source
|
|
if(istype(source, /datum/move_loop))
|
|
var/datum/move_loop/move_loop_source = source
|
|
charger = move_loop_source.moving
|
|
UnregisterSignal(charger, list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOVABLE_MOVED, COMSIG_LIVING_DEATH))
|
|
SEND_SIGNAL(owner, COMSIG_FINISHED_CHARGE)
|
|
actively_moving = FALSE
|
|
charging -= charger
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/update_status_on_signal(mob/source, new_stat, old_stat)
|
|
. = ..()
|
|
if(new_stat == DEAD)
|
|
GLOB.move_manager.stop_looping(source) //This will cause the loop to qdel, triggering an end to our charging
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/do_charge_indicator(atom/charger, atom/charge_target)
|
|
var/turf/target_turf = get_turf(charge_target)
|
|
if(!target_turf)
|
|
return
|
|
new /obj/effect/temp_visual/dragon_swoop/bubblegum(target_turf)
|
|
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(charger.loc, charger)
|
|
animate(D, alpha = 0, color = COLOR_RED, transform = matrix()*2, time = 3)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/on_move(atom/source, atom/new_loc)
|
|
SIGNAL_HANDLER
|
|
if(!actively_moving)
|
|
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
|
|
new /obj/effect/temp_visual/decoy/fading(source.loc, source)
|
|
INVOKE_ASYNC(src, PROC_REF(DestroySurroundings), source)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/on_moved(atom/source)
|
|
SIGNAL_HANDLER
|
|
playsound(source, 'sound/effects/meteorimpact.ogg', 200, TRUE, 2, TRUE)
|
|
INVOKE_ASYNC(src, PROC_REF(DestroySurroundings), source)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/DestroySurroundings(atom/movable/charger)
|
|
if(!destroy_objects)
|
|
return
|
|
if(!isanimal(charger))
|
|
return
|
|
for(var/dir in GLOB.cardinals)
|
|
var/turf/next_turf = get_step(charger, dir)
|
|
if(!next_turf)
|
|
continue
|
|
if(next_turf.Adjacent(charger) && (iswallturf(next_turf) || ismineralturf(next_turf)))
|
|
if(!isanimal(charger))
|
|
SSexplosions.medturf += next_turf
|
|
continue
|
|
next_turf.attack_animal(charger)
|
|
continue
|
|
for(var/obj/object in next_turf.contents)
|
|
if(!object.Adjacent(charger))
|
|
continue
|
|
if(!ismachinery(object) && !isstructure(object))
|
|
continue
|
|
if(!object.density || object.IsObscured())
|
|
continue
|
|
if(!isanimal(charger))
|
|
SSexplosions.med_mov_atom += target
|
|
break
|
|
object.attack_animal(charger)
|
|
break
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/on_bump(atom/movable/source, atom/target)
|
|
SIGNAL_HANDLER
|
|
if(owner == target)
|
|
return
|
|
if(destroy_objects)
|
|
if(isturf(target))
|
|
SSexplosions.medturf += target
|
|
if(isobj(target) && target.density)
|
|
SSexplosions.med_mov_atom += target
|
|
|
|
INVOKE_ASYNC(src, PROC_REF(DestroySurroundings), source)
|
|
try_hit_target(source, target)
|
|
|
|
/// Attempt to hit someone with our charge
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/try_hit_target(atom/movable/source, atom/target)
|
|
if (can_hit_target(source, target))
|
|
hit_target(source, target, charge_damage)
|
|
|
|
/// Returns true if we're allowed to charge into this target
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/can_hit_target(atom/movable/source, atom/target)
|
|
return isliving(target)
|
|
|
|
/// Actually hit someone
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/hit_target(atom/movable/source, mob/living/target, damage_dealt)
|
|
target.visible_message(span_danger("[source] slams into [target]!"), span_userdanger("[source] tramples you into the ground!"))
|
|
target.apply_damage(damage_dealt, BRUTE, wound_bonus = CANT_WOUND)
|
|
playsound(get_turf(target), 'sound/effects/meteorimpact.ogg', 100, TRUE)
|
|
shake_camera(target, 4, 3)
|
|
shake_camera(source, 2, 3)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/basic_charge
|
|
name = "Basic Charge"
|
|
cooldown_time = 6 SECONDS
|
|
charge_delay = 1.5 SECONDS
|
|
charge_distance = 4
|
|
melee_cooldown_time = 0
|
|
/// How long to shake for before charging
|
|
var/shake_duration = 1 SECONDS
|
|
/// Intensity of shaking animation
|
|
var/shake_pixel_shift = 2
|
|
/// Amount of time to stun self upon impact
|
|
var/recoil_duration = 0.6 SECONDS
|
|
/// Amount of time to knock over an impacted target
|
|
var/knockdown_duration = 0.6 SECONDS
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/basic_charge/do_charge_indicator(atom/charger, atom/charge_target)
|
|
charger.Shake(shake_pixel_shift, shake_pixel_shift, shake_duration)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/basic_charge/can_hit_target(atom/movable/source, atom/target)
|
|
if(!isliving(target))
|
|
if(!target.density || target.CanPass(source, get_dir(target, source)))
|
|
return FALSE
|
|
return TRUE
|
|
return ..()
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/basic_charge/hit_target(atom/movable/source, atom/target, damage_dealt)
|
|
var/mob/living/living_source
|
|
if(isliving(source))
|
|
living_source = source
|
|
|
|
if(!isliving(target))
|
|
source.visible_message(span_danger("[source] smashes into [target]!"))
|
|
living_source?.Stun(recoil_duration, ignore_canstun = TRUE)
|
|
return
|
|
|
|
var/mob/living/living_target = target
|
|
if(ishuman(living_target))
|
|
var/mob/living/carbon/human/human_target = living_target
|
|
if(human_target.check_block(source, 0, "the [source.name]", attack_type = LEAP_ATTACK) && living_source)
|
|
living_source.Stun(recoil_duration, ignore_canstun = TRUE)
|
|
return
|
|
|
|
living_target.visible_message(span_danger("[source] charges into [living_target]!"), span_userdanger("[source] charges into you!"))
|
|
living_target.Knockdown(knockdown_duration)
|
|
|
|
/datum/status_effect/tired_post_charge
|
|
id = "tired_post_charge"
|
|
duration = 1 SECONDS
|
|
alert_type = null
|
|
var/tired_movespeed = /datum/movespeed_modifier/status_effect/tired_post_charge
|
|
|
|
/datum/status_effect/tired_post_charge/on_apply()
|
|
. = ..()
|
|
owner.add_movespeed_modifier(tired_movespeed)
|
|
|
|
/datum/status_effect/tired_post_charge/on_remove()
|
|
. = ..()
|
|
owner.remove_movespeed_modifier(tired_movespeed)
|
|
|
|
/datum/status_effect/tired_post_charge/lesser
|
|
id = "tired_post_charge_easy"
|
|
tired_movespeed = /datum/movespeed_modifier/status_effect/tired_post_charge/lesser
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/triple_charge
|
|
name = "Triple Charge"
|
|
desc = "Allows you to charge three times at a chosen position."
|
|
charge_delay = 0.6 SECONDS
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/triple_charge/charge_sequence(atom/movable/charger, atom/target_atom, delay, past)
|
|
for(var/i in 0 to 2)
|
|
do_charge(owner, target_atom, charge_delay - 2 * i, charge_past)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge
|
|
name = "Hallucination Charge"
|
|
button_icon = 'icons/effects/bubblegum.dmi'
|
|
button_icon_state = "smack ya one"
|
|
desc = "Allows you to create hallucinations that charge around your target."
|
|
cooldown_time = 2 SECONDS
|
|
charge_delay = 0.6 SECONDS
|
|
/// The damage the hallucinations in our charge do
|
|
var/hallucination_damage = 15
|
|
/// Check to see if we are enraged, enraged ability does more
|
|
var/enraged = FALSE
|
|
/// Check to see if we should spawn blood
|
|
var/spawn_blood = FALSE
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/charge_sequence(atom/movable/charger, atom/target_atom, delay, past)
|
|
if(!enraged || prob(33))
|
|
hallucination_charge(target_atom, 6, 8, 0, 6, TRUE)
|
|
return
|
|
for(var/i in 0 to 2)
|
|
hallucination_charge(target_atom, 4, 9 - 2 * i, 0, 4, TRUE)
|
|
for(var/i in 0 to 2)
|
|
do_charge(owner, target_atom, charge_delay - 2 * i, charge_past)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/do_charge(atom/movable/charger, atom/target_atom, delay, past)
|
|
. = ..()
|
|
if(charger != owner)
|
|
qdel(charger)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/proc/hallucination_charge(atom/target_atom, clone_amount, delay, past, radius, use_self)
|
|
var/starting_angle = rand(1, 360)
|
|
if(!radius)
|
|
return
|
|
var/angle_difference = 360 / clone_amount
|
|
var/self_placed = FALSE
|
|
for(var/i = 1 to clone_amount)
|
|
var/angle = (starting_angle + angle_difference * i)
|
|
var/turf/place = locate(target_atom.x + cos(angle) * radius, target_atom.y + sin(angle) * radius, target_atom.z)
|
|
if(!place)
|
|
continue
|
|
if(use_self && !self_placed)
|
|
owner.forceMove(place)
|
|
self_placed = TRUE
|
|
continue
|
|
var/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/our_clone = new /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination(place)
|
|
our_clone.appearance = owner.appearance
|
|
our_clone.name = "[owner]'s hallucination"
|
|
our_clone.alpha = 127.5
|
|
our_clone.move_through_mob = owner
|
|
our_clone.spawn_blood = spawn_blood
|
|
INVOKE_ASYNC(src, PROC_REF(do_charge), our_clone, target_atom, delay, past)
|
|
if(use_self)
|
|
do_charge(owner, target_atom, delay, past)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/hit_target(atom/movable/source, atom/A, damage_dealt)
|
|
var/applied_damage = charge_damage
|
|
if(source != owner)
|
|
applied_damage = hallucination_damage
|
|
. = ..(source, A, applied_damage)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/hallucination_surround
|
|
name = "Surround Target"
|
|
button_icon = 'icons/mob/actions/actions_animal.dmi'
|
|
button_icon_state = "expand"
|
|
desc = "Allows you to create hallucinations that charge around your target."
|
|
charge_delay = 0.6 SECONDS
|
|
charge_past = 2
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/hallucination_surround/charge_sequence(atom/movable/charger, atom/target_atom, delay, past)
|
|
for(var/i in 0 to 4)
|
|
hallucination_charge(target_atom, 2, 8, 2, 2, FALSE)
|
|
do_charge(owner, target_atom, charge_delay, charge_past)
|