mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-21 21:10:30 +01:00
260f744906
This PR is a revisit to the previously derelict PR #20159 that has been unfinished for sometime now. More details about it in general can be found here: https://github.com/orgs/Aurorastation/projects/2?pane=issue&itemId=53167153 For awhile I've been talking about "Things I've been doing but it would be really nice to do them with a skills system", or "And here's how I would put this into the skills system when it's done". The main thing that was stopping me from building it myself was having poor real life skills in UI code and in DB code. However, I've gotten permission to resume this PR, which has already completed the steps I would not have been able to do myself. The rest of the PR fits well into my skillset as a dev. I'm opening this PR as a draft so as to enable my dev environment to locally track all the previously modified files. I'll take this PR out of draft and give this a full writeup when I have more work to show for the PR this weekend. ### TODO - [x] Rework a decent chunk of the currently existing skills to no longer require hardcoded inserts into other systems. EG, converting from classical ss13 methods, to modern /tg/-style ECS coding methods that work off of component-signal patterns. - [x] Make sure all of the existing skills have actual game functionality (I won't PR a 2016 Baystation12 situation where 90% of the skills are fluff only) - [x] Add the various skills not yet made but are necessary for completion sake, EG: Pilot (Spacecraft), Gunnery, Pilot (Walkers). - [x] Examine each existing job in the game and assess whether it should have a skill made with it in mind, or if it's covered by an existing skill. - [x] TO DISCUSS, BUT NOT ESSENTIAL: Additional skill proposals not currently in the pre-existing TODO list, proposing subcategories. - [x] Ensure that the previous TODO list is completed. ### Current Skills The current list of skills, checkmarked for if I've completed them/they have actual game mechanics. Or if we're just relegating them to separate PRs. Originally this list was going to be forced to visit for a bare minimum "does at least one thing" requirement, but now that is being forgone due to this PR ballooning out of control and in complexity, as well as development time overruns. - [x] Bartending - [x] Cooking - [x] Gardening - [x] Entertaining - [x] Electrical Engineering - [x] Mechanical Engineering - [x] Atmospherics Systems - [x] Reactor Systems - [x] Medicine - [x] Surgery - [x] Pharmacology - [x] Anatomy - [x] Forensics - [x] Robotics - [x] Pilot: Spacecraft - [x] Pilot: Exosuits - [x] Research - [x] Xenobotany - [x] Xenoarchaeology - [x] Xenobiology - [x] Unarmed Combat - [x] Armed Combat - [x] Firearms - [x] Leadership --------- Signed-off-by: VMSolidus <evilexecutive@gmail.com> Co-authored-by: Matt Atlas <liermattia@gmail.com> Co-authored-by: FabianK3 <21039694+FabianK3@users.noreply.github.com> Co-authored-by: Matt Atlas <mattiathebest2000@hotmail.it>
206 lines
9.1 KiB
Plaintext
206 lines
9.1 KiB
Plaintext
/*
|
|
=== Item Click Call Sequences ===
|
|
These are the default click code call sequences used when clicking on stuff with an item.
|
|
Atoms:
|
|
mob/ClickOn() calls the item's resolve_attackby() proc.
|
|
item/resolve_attackby() calls the target atom's attackby() proc.
|
|
Mobs:
|
|
mob/living/attackby() after checking for surgery, calls the item's attack() proc.
|
|
item/attack() generates attack logs, sets click cooldown and calls the mob's attacked_with_item() proc. If you override this, consider whether you need to set a click cooldown, play attack animations, and generate logs yourself.
|
|
mob/attacked_with_item() should then do mob-type specific stuff (like determining hit/miss, handling shields, etc) and then possibly call the item's apply_hit_effect() proc to actually apply the effects of being hit.
|
|
Item Hit Effects:
|
|
item/apply_hit_effect() can be overriden to do whatever you want. However "standard" physical damage based weapons should make use of the target mob's hit_with_weapon() proc to
|
|
avoid code duplication. This includes items that may sometimes act as a standard weapon in addition to having other effects (e.g. stunbatons on harm intent).
|
|
|
|
[obj/item/proc/base_item_interaction] handles all non-combat interactions of an item with an atom.
|
|
This calls [atom/proc/tool_act], among others.
|
|
|
|
*/
|
|
|
|
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
|
|
/obj/item/proc/attack_self(mob/user, modifiers)
|
|
if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user) & COMPONENT_CANCEL_ATTACK_CHAIN)
|
|
return TRUE
|
|
return
|
|
|
|
/// Called when the item is in the active hand, and right-clicked. Intended for alternate or opposite functions, such as lowering reagent transfer amount. At the moment, there is no verb or hotkey.
|
|
/obj/item/proc/attack_self_secondary(mob/user, modifiers)
|
|
if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF_SECONDARY, user) & COMPONENT_CANCEL_ATTACK_CHAIN)
|
|
return TRUE
|
|
|
|
// Called at the start of resolve_attackby(), before the actual attack.
|
|
/obj/item/proc/pre_attack(atom/a, mob/user)
|
|
return
|
|
|
|
//I would prefer to rename this to attack(), but that would involve touching hundreds of files.
|
|
/obj/item/proc/resolve_attackby(atom/A, mob/user, var/click_parameters)
|
|
pre_attack(A, user)
|
|
add_fingerprint(user)
|
|
log_attack("[A] at [A?.loc]/[A.x]-[A.y]-[A.z] got ITEM attacked by [usr]/[usr?.ckey] on INTENT [usr?.a_intent] with [src]")
|
|
return A.attackby(src, user, click_parameters)
|
|
|
|
/**
|
|
* Called on an object being hit by an item
|
|
*
|
|
* Returns `TRUE` if all desired actions are resolved from that attack
|
|
*
|
|
* Returning `TRUE` prevents `afterattack()` from being called
|
|
*
|
|
* * attacking_item - The item hitting the atom
|
|
* * user - The wielder of this item
|
|
* * params - Click params such as alt/shift etc
|
|
*/
|
|
/atom/proc/attackby(obj/item/attacking_item, mob/user, params)
|
|
if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACKBY, attacking_item, user, params) & COMPONENT_NO_AFTERATTACK)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/atom/movable/attackby(obj/item/attacking_item, mob/user, params)
|
|
if(..())
|
|
return TRUE
|
|
|
|
if((user?.a_intent == I_HURT) && !(attacking_item.item_flags & ITEM_FLAG_NO_BLUDGEON))
|
|
if(attacking_item.force && should_use_health && maxhealth)
|
|
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
|
|
user.do_attack_animation(src)
|
|
visible_message(SPAN_DANGER("[user] [pick(attacking_item.attack_verb)] \the [src]!"))
|
|
add_damage(attacking_item.force, attacking_item.damage_flags(), attacking_item.damtype, attacking_item.armor_penetration, attacking_item)
|
|
if(hitsound)
|
|
playsound(src, hitsound, attacking_item.get_clamped_volume(), 1, falloff_distance = 0)
|
|
else
|
|
playsound(src, attacking_item.hitsound, attacking_item.get_clamped_volume(), 1, falloff_distance = 0)
|
|
return FALSE
|
|
|
|
var/item_interact_result = src.base_item_interaction(user, attacking_item, modifiers)
|
|
if(item_interact_result & ITEM_INTERACT_SUCCESS)
|
|
return TRUE
|
|
if(item_interact_result & ITEM_INTERACT_BLOCKING)
|
|
return FALSE
|
|
|
|
/mob/living/attackby(obj/item/attacking_item, mob/user, params)
|
|
if(..())
|
|
return TRUE
|
|
|
|
if(!ismob(user))
|
|
return FALSE
|
|
|
|
var/selected_zone = user.zone_sel ? user.zone_sel.selecting : BP_CHEST
|
|
var/operating = can_operate(src)
|
|
if(operating == SURGERY_SUCCESS)
|
|
if(do_surgery(src, user, attacking_item))
|
|
return TRUE
|
|
else
|
|
return attacking_item.attack(src, user, selected_zone) //This is necessary to make things like health analyzers work. -mattatlas
|
|
if(operating == SURGERY_FAIL)
|
|
if(do_surgery(src, user, attacking_item, TRUE))
|
|
return TRUE
|
|
else
|
|
return attacking_item.attack(src, user, selected_zone)
|
|
else
|
|
return attacking_item.attack(src, user, selected_zone)
|
|
|
|
/mob/living/carbon/human/attackby(obj/item/attacking_item, mob/user, params)
|
|
if(user == src && user.a_intent == I_GRAB && zone_sel?.selecting == BP_MOUTH && can_devour(attacking_item, silent = TRUE))
|
|
var/obj/item/blocked = src.check_mouth_coverage()
|
|
if(blocked)
|
|
to_chat(user, SPAN_WARNING("\The [blocked] is in the way!"))
|
|
return TRUE
|
|
if(devour(attacking_item))
|
|
return TRUE
|
|
return ..()
|
|
|
|
// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
|
|
// Click parameters is the params string from byond Click() code, see that documentation.
|
|
/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
|
return
|
|
|
|
/obj/item/proc/get_clamped_volume()
|
|
if(w_class)
|
|
if(force)
|
|
return clamp((force + w_class) * 4, 30, 100)// Add the item's force to its weight class and multiply by 4, then clamp the value between 30 and 100
|
|
else
|
|
return clamp(w_class * 6, 10, 100) // Multiply the item's weight class by 6, then clamp the value between 10 and 100
|
|
|
|
/**
|
|
* Called from [/mob/living/proc/attackby] (usually)
|
|
*
|
|
* Arguments:
|
|
* * mob/living/target_mob - The mob being hit by this item
|
|
* * mob/living/user - The mob hitting with this item
|
|
* * target_zone - The target zone aimed at, **THIS DIFFERS FROM TG WHERE IT TAKES THE CLICK PARAMS**
|
|
*/
|
|
/obj/item/proc/attack(mob/living/target_mob, mob/living/user, target_zone = BP_CHEST)
|
|
var/signal_return = SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, target_mob, user) || SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, target_mob, user)
|
|
if(signal_return & COMPONENT_CANCEL_ATTACK_CHAIN)
|
|
return TRUE
|
|
if(signal_return & COMPONENT_SKIP_ATTACK)
|
|
return FALSE
|
|
|
|
if(item_flags & ITEM_FLAG_NO_BLUDGEON)
|
|
return 0
|
|
|
|
if(target_mob == user && user.a_intent != I_HURT)
|
|
return 0
|
|
|
|
if(user.incapacitated(INCAPACITATION_STUNNED|INCAPACITATION_KNOCKOUT|INCAPACITATION_KNOCKDOWN|INCAPACITATION_FORCELYING))
|
|
return
|
|
|
|
if(force && user.is_pacified())
|
|
to_chat(user, SPAN_WARNING("You don't want to harm other living beings!"))
|
|
return 0
|
|
|
|
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
|
|
user.do_attack_animation(target_mob, src)
|
|
if(!user.aura_check(AURA_TYPE_WEAPON, src, user))
|
|
return FALSE
|
|
|
|
var/mob/living/victim = target_mob.get_attack_victim(src, user, target_zone)
|
|
var/hit_zone
|
|
if(victim)
|
|
hit_zone = victim.resolve_item_attack(src, user, target_zone)
|
|
if(hit_zone)
|
|
apply_hit_effect(victim, user, hit_zone)
|
|
|
|
// Null hitzone means a miss.
|
|
if(hit_zone)
|
|
if(!force)
|
|
playsound(src, 'sound/weapons/tap.ogg', get_clamped_volume(), TRUE, -1)
|
|
else if(hitsound)
|
|
playsound(src, hitsound, get_clamped_volume(), TRUE, -1, falloff_distance = 0)
|
|
else
|
|
playsound(src, 'sound/weapons/punchmiss2.ogg', get_clamped_volume(), TRUE, -1)
|
|
|
|
/////////////////////////
|
|
user.lastattacked = target_mob
|
|
target_mob.lastattacker = user
|
|
|
|
SEND_SIGNAL(src, COMSIG_ITEM_AFTERATTACK, target_mob, user)
|
|
SEND_SIGNAL(target_mob, COMSIG_ATOM_AFTER_ATTACKEDBY, src, user)
|
|
|
|
if(!no_attack_log)
|
|
user.attack_log += "\[[time_stamp()]\]<span class='warning'> [hit_zone ? "Attacked" : "Missed"] [target_mob.name] ([target_mob.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])</span>"
|
|
target_mob.attack_log += "\[[time_stamp()]\]<font color='orange'> [hit_zone ? "Attacked" : "Missed"] by [user.name] ([user.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])</font>"
|
|
msg_admin_attack("[key_name(user, highlight_special = 1)] [hit_zone ? "attacked" : "missed"] [key_name(target_mob, highlight_special = 1)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)]) (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)",ckey=key_name(user),ckey_target=key_name(target_mob) )
|
|
/////////////////////////
|
|
|
|
return 1
|
|
|
|
//Called when a weapon is used to make a successful melee attack on a mob. Returns whether damage was dealt.
|
|
/obj/item/proc/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone)
|
|
var/power = force
|
|
SEND_SIGNAL(user, COMSIG_APPLY_HIT_EFFECT, target, src, &power, &hit_zone)
|
|
if((user.mutations & HULK))
|
|
power *= 1.25
|
|
if(user.is_berserk())
|
|
power *= 1.1
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/X = user
|
|
if(ishuman(target))
|
|
if(X.check_weapon_affinity(src))
|
|
perform_technique(target, X, hit_zone)
|
|
|
|
return target.hit_with_weapon(src, user, power, hit_zone)
|
|
|
|
/obj/item/proc/perform_technique(var/mob/living/carbon/human/target, var/mob/living/carbon/human/user, var/target_zone) //used when weapons have special interactions with martial arts
|
|
return
|