From b7d8a930777ed41ba8beb3512a9292d2b4015f52 Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 28 Jan 2023 01:38:41 -0500 Subject: [PATCH 01/26] baton katana and spear --- .../mecha/equipment/weapons/melee_weapons.dm | 140 +++++++++++++++++- 1 file changed, 137 insertions(+), 3 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index ad5f790c20d2..a41e06f31513 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -27,9 +27,11 @@ var/weapon_damage = 0 //If we have both cleave and precise attacks, the precise may have more damage var/precise_weapon_damage = 0 + //Minimum damage dealt with a weapon. Applies only to non-combat mechs to make them suck a little less + var/minimum_damage = 0 //Bonus deflection chance for using a melee weapon capable of blocking attacks var/deflect_bonus = 0 - //Base armor piercing value of the weapon + //Base armor piercing value of the weapon. This value is doubled for precise attacks var/base_armor_piercing = 0 //Fauna bonus damage, if any var/fauna_damage_bonus = 0 @@ -80,6 +82,9 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/proc/cleave_attack() return 0 +/obj/item/mecha_parts/mecha_equipment/melee_weapon/proc/special_hit() //For special effects, slightly simplifies cleave/precise attack procs + return 0 + /obj/item/mecha_parts/mecha_equipment/melee_weapon/on_select() if(deflect_bonus) chassis.deflect_chance += deflect_bonus @@ -88,6 +93,11 @@ if(deflect_bonus) chassis.deflect_chance -= deflect_bonus + // //=========================================================\\ + //======|| SWORDS AND SWORD-ADJACENTS || + // \\=========================================================// + +//Standard melee weapon, easily modifiable to suit your needs /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword name = "generic mech sword" desc = "Generic mech sword! It's a bit too big to use yourself." @@ -96,7 +106,11 @@ attack_sharpness = SHARP_EDGED attack_sound = 'sound/weapons/mechasword.ogg' //Recorded from Respawn/EA's Titanfall 2 (Ronin broadsword swing). Apparently they don't care so we're probably good harmful = TRUE //DO NOT give to children. Or do, I'm not the police. - var/minimum_damage = 0 //Baby mechs with a secret combat module get a little boost + minimum_damage = 0 //Baby mechs with a secret combat module get a little boost + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/proc/special_hit(atom/target) + return 0 + /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/shortsword //Our bread-and-butter mech shortsword for both slicing and stabbing baddies name = "\improper GD6 \"Jaeger\" shortsword" @@ -157,7 +171,7 @@ if(iscarbon(L)) var/mob/living/carbon/C = L - var/obj/item/bodypart/body_part = chassis.occupant.zone_selected + var/obj/item/bodypart/body_part = L.get_bodypart(chassis.occupant? chassis.occupant.zone_selected : BODY_ZONE_CHEST) var/armor_block = C.run_armor_check(body_part, MELEE, armour_penetration = base_armor_piercing * 2) //and get more AP C.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness) else @@ -201,6 +215,7 @@ var/it_turn = 45*(1-i) var/turf/T = get_step(M,turn(chassis.dir, it_turn)) //+45, +0, and -45 will get the three front tiles for(var/atom/A in T.contents) + special_hit(A) if(isliving(A)) var/mob/living/L = A @@ -247,6 +262,69 @@ STOP_PROCESSING(SSobj, src) set_light_on(FALSE) +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/katana //Anime mech sword + name = "\improper OWM-5 \"Ronin\" katana" + desc = "An oversized, light-weight replica of an ancient style of blade. Still woefully underpowered in D&D." + energy_drain = 20 + precise_weapon_damage = 10 //noticeably less damage than its larger cousin + cleave = FALSE //small fast blade + attack_speed_modifier = 0.7 //live out your anime dreams in a mech + fauna_damage_bonus = 20 //because why not + deflect_bonus = 15 + base_armor_piercing = 20 + structure_damage_mult = 2 //katana is less smashy than other swords + minimum_damage = 20 + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong + name = "\improper I Asked Mord For A Name And He Didnt Give Me One Yet" //FIND NAME <===========================================================!!!!!! + desc = "A stun baton, but bigger. The tide of toolbox-armed assistants don't stand a chance." + energy_drain = 300 + attack_speed_modifier = 1.4 //needs to recharge + structure_damage_mult = 1 + precise_weapon_damage = -20 + weapon_damage = -20 + minimum_damage = 10 + var/special_hit_stamina_damage = 80 //A bit stronger than a normal baton + var/stunforce = 10 SECONDS + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong/on_select() + chassis.force /= 3 //Nonlethal weapon, don't want to outright kill people with it if we're swinging with a combat mech + . = ..() + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong/on_deselect() + chassis.force *= 3 + . = ..() + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong/special_hit(atom/target) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + var/obj/item/bodypart/affecting = H.get_bodypart(BODY_ZONE_CHEST) //We're smacking them square in the chest with a giant stun stick + var/armor_block = H.run_armor_check(affecting, ENERGY) + H.apply_damage(special_hit_stamina_damage, STAMINA, BODY_ZONE_CHEST, armor_block) + SEND_SIGNAL(H, COMSIG_LIVING_MINOR_SHOCK) + + var/current_stamina_damage = H.getStaminaLoss() + if(current_stamina_damage >= 90) + if(!H.IsParalyzed()) + to_chat(H, span_warning("You muscles seize, making you collapse!")) + else + H.Paralyze(stunforce) + H.Jitter(20) + H.confused = max(8, H.confused) + H.apply_effect(EFFECT_STUTTER, stunforce) + else if(current_stamina_damage > 70) + H.Jitter(10) + H.confused = max(8, H.confused) + H.apply_effect(EFFECT_STUTTER, stunforce) + else if(current_stamina_damage >= 20) + H.Jitter(5) + H.apply_effect(EFFECT_STUTTER, stunforce) + + playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) + + else + return + /obj/item/mecha_parts/mecha_equipment/melee_weapon/rocket_fist //Passive upgrade weapon when selected, makes your mech punch harder AND faster name = "\improper DD-2 \"Atom Smasher\" rocket fist" desc = "A large metal fist fitted to the arm of an exosuit, it uses repurposed maneuvering thrusters from a Raven battlecruiser to give a little more oomph to every punch. Also helps increase the speed at which the mech is able to return to a ready stance after each swing." @@ -264,3 +342,59 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/rocket_fist/on_deselect() chassis.force -= weapon_damage //Return to babby fist chassis.melee_cooldown /= 0.8 + + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/spear + name = "\improper S5-C \"White Witch\" shortspear" + desc = "A hardened, telescoping metal rod with a wicked-sharp tip. Perfect for punching holes in things normally out of reach." + icon_state = "mecha_spear" + energy_drain = 30 + force = 10 //I want someone to stab someone else with this by hand + extended_range = 1 //Hits from a tile away + precise_weapon_damage = 15 + minimum_damage = 20 //No bonus for weak mechs + attack_speed_modifier = 1.2 //Slightly slower + attack_sharpness = SHARP_POINTY + sharpness = SHARP_POINTY //You can't use it well but it IS still a giant sharp metal stick + base_armor_piercing = 40 + structure_damage_mult = 1.5 //Not great at destroying stuff + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/spear/precise_attack(atom/target, penetrated = FALSE) + if(!penetrated) + //Find some way to get all the stuff in a line between attacker and target and hit them all + if(isliving(target)) + var/mob/living/L = target + + if(iscarbon(L)) + var/mob/living/carbon/C = L + var/obj/item/bodypart/body_part = L.get_bodypart(penetrated ? BODY_ZONE_CHEST : chassis.occupant ? chassis.occupant.zone_selected : BODY_ZONE_CHEST) + var/armor_block = C.run_armor_check(body_part, MELEE, armour_penetration = base_armor_piercing) + C.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness) + else + L.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type) + if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) //Stab them harder + L.apply_damage(fauna_damage_bonus, dam_type) + + L.visible_message(span_danger("[chassis.name] stabs [L] with [src]!"), \ + span_userdanger("[chassis.name] stabs you with [src]!")) + chassis.log_message("Hit [L] with [src.name] (precise attack).", LOG_MECHA) + + else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha)) //If the initial target is a big object, hit it even if it's not dense. + var/obj/O = target + var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult + O.take_damage(object_damage, dam_type, "melee", 0) + if(istype(target, /obj/mecha)) + special_hit(target) + else + return + chassis.do_attack_animation(target, ATTACK_EFFECT_KICK) + playsound(chassis, attack_sound, 50, 1) + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/spear/special_hit(/obj/mecha/target) + if(ishuman(T.occupant)) + var/mob/living/carbon/human/H = T.occupant + precise_attack(H, TRUE) + H.visible_message(span_danger("[chassis.name] stabs [H] with [src]!"), \ + span_userdanger("[chassis.name] penetrates your suits armor with [src]!")) + chassis.log_message("Hit [L] with [src.name] (precise attack).", LOG_MECHA) + From c3daf2394c0f620110be9e15c464301c17d049b0 Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 28 Jan 2023 14:09:58 -0500 Subject: [PATCH 02/26] tweaks --- .../mecha/equipment/weapons/melee_weapons.dm | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index a41e06f31513..2de27a4b4b6a 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -108,7 +108,7 @@ harmful = TRUE //DO NOT give to children. Or do, I'm not the police. minimum_damage = 0 //Baby mechs with a secret combat module get a little boost -/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/proc/special_hit(atom/target) +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/special_hit(atom/target) return 0 @@ -281,20 +281,12 @@ energy_drain = 300 attack_speed_modifier = 1.4 //needs to recharge structure_damage_mult = 1 - precise_weapon_damage = -20 + precise_weapon_damage = -20 //Mostly nonlethal weapon_damage = -20 minimum_damage = 10 var/special_hit_stamina_damage = 80 //A bit stronger than a normal baton var/stunforce = 10 SECONDS -/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong/on_select() - chassis.force /= 3 //Nonlethal weapon, don't want to outright kill people with it if we're swinging with a combat mech - . = ..() - -/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong/on_deselect() - chassis.force *= 3 - . = ..() - /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong/special_hit(atom/target) if(ishuman(target)) var/mob/living/carbon/human/H = target @@ -325,6 +317,12 @@ else return + + // //=========================================================\\ + //======|| SNOWFLAKE WEAPONS THAT ARENT SWORDS || + // \\=========================================================// + + /obj/item/mecha_parts/mecha_equipment/melee_weapon/rocket_fist //Passive upgrade weapon when selected, makes your mech punch harder AND faster name = "\improper DD-2 \"Atom Smasher\" rocket fist" desc = "A large metal fist fitted to the arm of an exosuit, it uses repurposed maneuvering thrusters from a Raven battlecruiser to give a little more oomph to every punch. Also helps increase the speed at which the mech is able to return to a ready stance after each swing." @@ -391,10 +389,10 @@ playsound(chassis, attack_sound, 50, 1) /obj/item/mecha_parts/mecha_equipment/melee_weapon/spear/special_hit(/obj/mecha/target) - if(ishuman(T.occupant)) - var/mob/living/carbon/human/H = T.occupant + if(ishuman(target.occupant)) + var/mob/living/carbon/human/H = target.occupant precise_attack(H, TRUE) H.visible_message(span_danger("[chassis.name] stabs [H] with [src]!"), \ span_userdanger("[chassis.name] penetrates your suits armor with [src]!")) - chassis.log_message("Hit [L] with [src.name] (precise attack).", LOG_MECHA) + chassis.log_message("Hit [H] with [src.name] (precise attack).", LOG_MECHA) From dda40e43dd312f09fad45b8913d3728e0eb54a52 Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 28 Jan 2023 15:00:10 -0500 Subject: [PATCH 03/26] e --- .../mecha/equipment/weapons/melee_weapons.dm | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 2de27a4b4b6a..5a909a126a84 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -27,7 +27,7 @@ var/weapon_damage = 0 //If we have both cleave and precise attacks, the precise may have more damage var/precise_weapon_damage = 0 - //Minimum damage dealt with a weapon. Applies only to non-combat mechs to make them suck a little less + //Minimum damage dealt with a weapon. Applies to non-combat mechs with the secret compartment module to make them suck a little less var/minimum_damage = 0 //Bonus deflection chance for using a melee weapon capable of blocking attacks var/deflect_bonus = 0 @@ -38,6 +38,9 @@ //Structure damage multiplier, for stuff like big ol' smashy hammers. Base structure damage multiplier for mech melee attacks is 3. var/structure_damage_mult = 3 + //Effect on hitting something + var/hit_effect = ATTACK_EFFECT_SLASH + //Effect of the cleave attack var/cleave_effect = /obj/effect/temp_visual/dir_setting/firing_effect/mecha_swipe /obj/item/mecha_parts/mecha_equipment/melee_weapon/can_attach(obj/mecha/M) @@ -106,7 +109,7 @@ attack_sharpness = SHARP_EDGED attack_sound = 'sound/weapons/mechasword.ogg' //Recorded from Respawn/EA's Titanfall 2 (Ronin broadsword swing). Apparently they don't care so we're probably good harmful = TRUE //DO NOT give to children. Or do, I'm not the police. - minimum_damage = 0 //Baby mechs with a secret combat module get a little boost + minimum_damage = 0 /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/special_hit(atom/target) return 0 @@ -141,7 +144,7 @@ C.apply_damage(max(chassis.force + weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness) else //Regular mobs just take damage L.apply_damage(max(chassis.force + weapon_damage, minimum_damage), dam_type) - if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) //If we're hitting fauna, because heck those guys + if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid) && fauna_damage_bonus) //If we're hitting fauna, because heck those guys L.apply_damage(fauna_damage_bonus, dam_type) L.visible_message(span_danger("[chassis.name] strikes [L] with a wide swing of [src]!"), \ @@ -189,7 +192,7 @@ O.take_damage(object_damage, dam_type, "melee", 0) else return - chassis.do_attack_animation(target, ATTACK_EFFECT_SLASH) + chassis.do_attack_animation(target, hit_effect) playsound(chassis, attack_sound, 50, 1) /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/energy_axe @@ -226,7 +229,7 @@ C.apply_damage(max(chassis.force + weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness) else //Regular mobs just take damage L.apply_damage(max(chassis.force + weapon_damage, minimum_damage), dam_type) - if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) //If we're hitting fauna, because heck those guys + if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid) && fauna_damage_bonus) //If we're hitting fauna, because heck those guys L.apply_damage(fauna_damage_bonus, dam_type) L.visible_message(span_danger("[chassis.name] strikes [L] with a wide swing of [src]!"), \ @@ -276,18 +279,20 @@ minimum_damage = 20 /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong - name = "\improper I Asked Mord For A Name And He Didnt Give Me One Yet" //FIND NAME <===========================================================!!!!!! + name = "\improper AV-98 \"Ingram\" heavy stun baton" desc = "A stun baton, but bigger. The tide of toolbox-armed assistants don't stand a chance." energy_drain = 300 - attack_speed_modifier = 1.4 //needs to recharge + attack_speed_modifier = 1.5 //needs to recharge structure_damage_mult = 1 precise_weapon_damage = -20 //Mostly nonlethal weapon_damage = -20 minimum_damage = 10 + hit_effect = ATTACK_EFFECT_BOOP //Boop :) + attack_sharpness = SHARP_NONE var/special_hit_stamina_damage = 80 //A bit stronger than a normal baton var/stunforce = 10 SECONDS -/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong/special_hit(atom/target) +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong/special_hit(atom/target) //It's a stun baton. It stuns. if(ishuman(target)) var/mob/living/carbon/human/H = target var/obj/item/bodypart/affecting = H.get_bodypart(BODY_ZONE_CHEST) //We're smacking them square in the chest with a giant stun stick @@ -349,17 +354,19 @@ energy_drain = 30 force = 10 //I want someone to stab someone else with this by hand extended_range = 1 //Hits from a tile away - precise_weapon_damage = 15 - minimum_damage = 20 //No bonus for weak mechs + precise_weapon_damage = 10 + minimum_damage = 20 attack_speed_modifier = 1.2 //Slightly slower attack_sharpness = SHARP_POINTY sharpness = SHARP_POINTY //You can't use it well but it IS still a giant sharp metal stick base_armor_piercing = 40 structure_damage_mult = 1.5 //Not great at destroying stuff + hit_effect = ATTACK_EFFECT_KICK //Don't question it + /obj/item/mecha_parts/mecha_equipment/melee_weapon/spear/precise_attack(atom/target, penetrated = FALSE) - if(!penetrated) - //Find some way to get all the stuff in a line between attacker and target and hit them all + //if(!penetrated) + //Find some way to get all the stuff in a line between attacker and target and hit them all, but this can wait if(isliving(target)) var/mob/living/L = target @@ -370,7 +377,7 @@ C.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness) else L.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type) - if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) //Stab them harder + if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid) && fauna_damage_bonus) //Stab them harder L.apply_damage(fauna_damage_bonus, dam_type) L.visible_message(span_danger("[chassis.name] stabs [L] with [src]!"), \ @@ -385,14 +392,16 @@ special_hit(target) else return - chassis.do_attack_animation(target, ATTACK_EFFECT_KICK) + chassis.do_attack_animation(target, hit_effect) playsound(chassis, attack_sound, 50, 1) -/obj/item/mecha_parts/mecha_equipment/melee_weapon/spear/special_hit(/obj/mecha/target) - if(ishuman(target.occupant)) - var/mob/living/carbon/human/H = target.occupant - precise_attack(H, TRUE) - H.visible_message(span_danger("[chassis.name] stabs [H] with [src]!"), \ - span_userdanger("[chassis.name] penetrates your suits armor with [src]!")) - chassis.log_message("Hit [H] with [src.name] (precise attack).", LOG_MECHA) +/obj/item/mecha_parts/mecha_equipment/melee_weapon/spear/special_hit(atom/target) //Pierces mechs and hits the pilot + if(ismecha(target)) + var/obj/mecha/M = target + if(ishuman(M.occupant)) + var/mob/living/carbon/human/H = M.occupant + precise_attack(H, TRUE) + H.visible_message(span_danger("[chassis.name] stabs [H] with [src]!"), \ + span_userdanger("[chassis.name] penetrates your suits armor with [src]!")) + chassis.log_message("Hit [H] with [src.name] (precise attack).", LOG_MECHA) From 98d962077c6b3b2fd8e14bb9297f3648cef2ea82 Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 28 Jan 2023 15:00:34 -0500 Subject: [PATCH 04/26] ee --- code/game/mecha/equipment/weapons/melee_weapons.dm | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 5a909a126a84..5628679e4254 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -107,8 +107,9 @@ cleave = TRUE precise_attacks = TRUE attack_sharpness = SHARP_EDGED - attack_sound = 'sound/weapons/mechasword.ogg' //Recorded from Respawn/EA's Titanfall 2 (Ronin broadsword swing). Apparently they don't care so we're probably good - harmful = TRUE //DO NOT give to children. Or do, I'm not the police. + attack_sound = 'sound/weapons/mechasword.ogg' //Recorded from Respawn/EA's Titanfall 2 (Ronin broadsword swing). Apparently they don't care so we're probably good + var/mob_strike_sound = 'sound/weapons/bladeslice.ogg' //The sound it makes when the cleave hits a mob, different from the attack + harmful = TRUE //DO NOT give to children. Or do, I'm not the police. minimum_damage = 0 /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/special_hit(atom/target) @@ -150,7 +151,7 @@ L.visible_message(span_danger("[chassis.name] strikes [L] with a wide swing of [src]!"), \ span_userdanger("[chassis.name] strikes you with [src]!")) chassis.log_message("Hit [L] with [src.name] (cleave attack).", LOG_MECHA) - playsound(L, 'sound/weapons/bladeslice.ogg', 50) + playsound(L, mob_strike_sound, 50) else if(isstructure(A) || ismachinery(A) || istype(A, /obj/mecha)) //if it's something we can otherwise still hit var/obj/O = A @@ -207,6 +208,7 @@ structure_damage_mult = 4 //Think obi-wan cutting through a bulkhead with his lightsaber but he's a giant mech with a huge terrifying axe minimum_damage = 40 attack_speed_modifier = 1.5 //Kinda chunky + mob_strike_sound = 'sound/weapons/blade1.ogg' light_system = MOVABLE_LIGHT light_range = 5 light_color = LIGHT_COLOR_RED @@ -235,7 +237,7 @@ L.visible_message(span_danger("[chassis.name] strikes [L] with a wide swing of [src]!"), \ span_userdanger("[chassis.name] strikes you with [src]!")) chassis.log_message("Hit [L] with [src.name] (cleave attack).", LOG_MECHA) - playsound(L, 'sound/weapons/blade1.ogg', 50) + playsound(L, mob_strike_sound, 50) else if(isstructure(A) || ismachinery(A) || istype(A, /obj/mecha)) //if it's something we can otherwise still hit var/obj/O = A @@ -289,6 +291,7 @@ minimum_damage = 10 hit_effect = ATTACK_EFFECT_BOOP //Boop :) attack_sharpness = SHARP_NONE + mob_strike_sound = 'sound/weapons/egloves.ogg' var/special_hit_stamina_damage = 80 //A bit stronger than a normal baton var/stunforce = 10 SECONDS @@ -317,8 +320,6 @@ H.Jitter(5) H.apply_effect(EFFECT_STUTTER, stunforce) - playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) - else return From aa68bfdf28ceda3e4a0cc1f537305858279d7670 Mon Sep 17 00:00:00 2001 From: Mek Date: Tue, 31 Jan 2023 04:11:28 -0500 Subject: [PATCH 05/26] lets GO --- .../mecha/equipment/weapons/melee_weapons.dm | 128 ++++++++++-------- 1 file changed, 70 insertions(+), 58 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 5649b1c9b2df..3482028c1006 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -86,7 +86,7 @@ return 0 /obj/item/mecha_parts/mecha_equipment/melee_weapon/proc/special_hit() //For special effects, slightly simplifies cleave/precise attack procs - return 0 + return 1 /obj/item/mecha_parts/mecha_equipment/melee_weapon/on_select() if(deflect_bonus) @@ -110,7 +110,8 @@ attack_sound = 'sound/weapons/mechasword.ogg' //Recorded from Respawn/EA's Titanfall 2 (Ronin broadsword swing). Apparently they don't care so we're probably good var/mob_strike_sound = 'sound/weapons/bladeslice.ogg' //The sound it makes when the cleave hits a mob, different from the attack harmful = TRUE //DO NOT give to children. Or do, I'm not the police. - minimum_damage = 0 + minimum_damage = 0 + var/sword_wound_bonus = 0 //Wound bonus if it's supposed to be ouchy beyond just doing damage /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/special_hit(atom/target) return 0 @@ -138,11 +139,11 @@ if(isliving(A)) var/mob/living/L = A - if(iscarbon(L)) //If we're a carbon we can get armor and jazz + if(iscarbon(L)) //If we're a carbon we can get armor and jazz var/mob/living/carbon/C = L var/obj/item/bodypart/body_part = pick(C.bodyparts) //Cleave attack isn't very precise var/armor_block = C.run_armor_check(body_part, MELEE, armour_penetration = base_armor_piercing) - C.apply_damage(max(chassis.force + weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness) + C.apply_damage(max(chassis.force + weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness, wound_bonus = sword_wound_bonus) else //Regular mobs just take damage L.apply_damage(max(chassis.force + weapon_damage, minimum_damage), dam_type) if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid) && fauna_damage_bonus) //If we're hitting fauna, because heck those guys @@ -155,7 +156,7 @@ else if(isstructure(A) || ismachinery(A) || istype(A, /obj/mecha)) //if it's something we can otherwise still hit var/obj/O = A - if(!O.density) //Make sure it's not an open door or something + if(!O.density) //Make sure it's not an open door or something continue var/object_damage = max(chassis.force + weapon_damage, minimum_damage) * structure_damage_mult O.take_damage(object_damage, dam_type, "melee", 0) @@ -164,20 +165,21 @@ else if(istype(A, /obj/mecha)) O.visible_message(span_danger("[chassis.name] strikes [O] with a wide swing of [src]!")) //Don't really need to make a message for EVERY object, just important ones - playsound(O,'sound/weapons/smash.ogg', 50) //metallic bonk noise + playsound(O,'sound/weapons/smash.ogg', 50) //metallic bonk noise var/turf/cleave_effect_loc = get_step(get_turf(src), SOUTHWEST) new cleave_effect(cleave_effect_loc, chassis.dir) /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/precise_attack(atom/target) + special_hit(target) if(isliving(target)) var/mob/living/L = target if(iscarbon(L)) var/mob/living/carbon/C = L var/obj/item/bodypart/body_part = L.get_bodypart(chassis.occupant? chassis.occupant.zone_selected : BODY_ZONE_CHEST) - var/armor_block = C.run_armor_check(body_part, MELEE, armour_penetration = base_armor_piercing * 2) //and get more AP - C.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness) + var/armor_block = C.run_armor_check(body_part, MELEE, armour_penetration = base_armor_piercing * 2) //more AP for precision attacks + C.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness, wound_bonus = sword_wound_bonus) else L.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type) if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) //Stab them harder @@ -213,51 +215,11 @@ light_range = 5 light_color = LIGHT_COLOR_RED -/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/energy_axe/cleave_attack() //Mostly copy-pasted sword cleave code with minor tweaks. - playsound(chassis, attack_sound, 50, 1) //Not hard to change some of this to be based on vars if you REALLY wanted to - var/turf/M = get_turf(src) - for(var/i = 0 to 2) - var/it_turn = 45*(1-i) - var/turf/T = get_step(M,turn(chassis.dir, it_turn)) //+45, +0, and -45 will get the three front tiles - for(var/atom/A in T.contents) - special_hit(A) - if(isliving(A)) - var/mob/living/L = A - - if(iscarbon(L)) //If we're a carbon we can get armor and jazz - var/mob/living/carbon/C = L - var/obj/item/bodypart/body_part = pick(C.bodyparts) //Cleave attack isn't very precise - var/armor_block = C.run_armor_check(body_part, MELEE, armour_penetration = base_armor_piercing) - C.apply_damage(max(chassis.force + weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness) - else //Regular mobs just take damage - L.apply_damage(max(chassis.force + weapon_damage, minimum_damage), dam_type) - if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid) && fauna_damage_bonus) //If we're hitting fauna, because heck those guys - L.apply_damage(fauna_damage_bonus, dam_type) - - L.visible_message(span_danger("[chassis.name] strikes [L] with a wide swing of [src]!"), \ - span_userdanger("[chassis.name] strikes you with [src]!")) - chassis.log_message("Hit [L] with [src.name] (cleave attack).", LOG_MECHA) - playsound(L, mob_strike_sound, 50) - - else if(isstructure(A) || ismachinery(A) || istype(A, /obj/mecha)) //if it's something we can otherwise still hit - var/obj/O = A - if(!O.density) //Make sure it's not an open door or something - continue - var/object_damage = max(chassis.force + weapon_damage, minimum_damage) * structure_damage_mult - O.take_damage(object_damage, dam_type, "melee", 0) - if(istype(O, /obj/structure/window)) - playsound(O,'sound/effects/Glasshit.ogg', 50) //glass bonk noise - else - if(istype(A, /obj/mecha)) - O.visible_message(span_danger("[chassis.name] strikes [O] with a wide swing of [src]!")) - playsound(O,'sound/weapons/smash.ogg', 50) //metallic bonk noise - - if(istype(T, /turf/closed/wall)) //IT BREAKS WALLS TOO - var/turf/closed/wall/W = T - W.dismantle_wall() - - var/turf/cleave_effect_loc = get_step(get_turf(src), SOUTHWEST) //Big sprite needs to be centered properly - new cleave_effect(cleave_effect_loc, chassis.dir) +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/energy_axe/cleave_attack(A) + if(istype(A, /turf/closed/wall)) //IT BREAKS WALLS TOO + var/turf/closed/wall/W = A + playsound(W, 'sound/weapons/blade1.ogg', 50) + W.dismantle_wall() /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/energy_axe/on_select() START_PROCESSING(SSobj, src) @@ -270,15 +232,16 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/katana //Anime mech sword name = "\improper OWM-5 \"Ronin\" katana" desc = "An oversized, light-weight replica of an ancient style of blade. Still woefully underpowered in D&D." - energy_drain = 20 + energy_drain = 15 precise_weapon_damage = 10 //noticeably less damage than its larger cousin cleave = FALSE //small fast blade attack_speed_modifier = 0.7 //live out your anime dreams in a mech fauna_damage_bonus = 20 //because why not deflect_bonus = 15 base_armor_piercing = 20 - structure_damage_mult = 2 //katana is less smashy than other swords - minimum_damage = 20 + structure_damage_mult = 1.5 //katana is less smashy than other swords + minimum_damage = 20 + sword_wound_bonus = 15 //More bleeding /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong name = "\improper AV-98 \"Ingram\" heavy stun baton" @@ -319,9 +282,58 @@ else if(current_stamina_damage >= 20) H.Jitter(5) H.apply_effect(EFFECT_STUTTER, stunforce) + + if(isliving(target)) + step_away(src, target) //We push all mobs back a tad - else - return + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/trogdor //TROGDOR!!!!! (But he's not a robot so I can't make the visible name that) + name = "\improper TO-4 \"Tahu\" flaming chainsword" //ITS ALSO A CHAINSWORD FUCK YEAH + desc = "It's as ridiculous as it is badass. You feel like use of this this might be considered a war crime somewhere." + energy_drain = 30 + precise_weapon_damage = 5 //Gotta make space for the burninating + attack_speed_modifier = 1.2 //Little unwieldy + fauna_damage_bonus = 20 + base_armor_piercing = 15 + structure_damage_mult = 3.5 //It melts AND cuts! + sword_wound_bonus = -30 //We're here for the fire damage thank you + var/burninating = 15 //BURNINATING THE COUNTRYSIDE + mob_strike_sound = 'sound/weapons/chainsawhit.ogg' + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/trogdor/special_hit(atom/A) + if(isliving(A)) + var/mob/living/L = A + var/armor_block = L.run_armor_check(BODY_ZONE_CHEST, MELEE) //it's a sword so we check melee armor + var/burn_damage = (100-armor_block)/100 * burninating + if(iscarbon(A)) + var/mob/living/carbon/C = L + C.adjustFireLoss(burn_damage) + else + L.apply_damage(burn_damage, BURN) + L.adjust_fire_stacks(5) + L.IgniteMob() + playsound(L, 'sound/items/welder.ogg', 50, 1) + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/maul + name = "\improper CX-22 \"Barbados\" heavy maul" + desc = "A massive, unwieldy, mace-like weapon, this thing really looks like something you don't want to be hit by if you're not a fan of being concave." + energy_drain = 40 + weapon_damage = 25 //Very smashy + precise_weapon_damage = 30 + attack_speed_modifier = 2 //Very slow + fauna_damage_bonus = 40 + structure_damage_mult = 4 //Good for stationary objects + attack_sharpness = SHARP_NONE + sword_wound_bonus = 20 //Makes bones sad :( + mob_strike_sound = 'sound/effects/meteorimpact.ogg' //WHAM + hit_effect = ATTACK_EFFECT_SMASH //POW + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/maul/special_hit(atom/A) + if(isliving(A)) + var/mob/living/L = A + var/throwtarget = get_edge_target_turf(L, get_dir(L, get_step_away(src, L))) + L.throw_at(throwtarget, 8, 2, src) //Get outta here! + do_item_attack_animation(L, hit_effect) // //=========================================================\\ From 2b8b239a578678d532034cdd069e17f1e0bf3d3c Mon Sep 17 00:00:00 2001 From: Mek Date: Tue, 31 Jan 2023 04:32:17 -0500 Subject: [PATCH 06/26] slower --- code/game/mecha/equipment/weapons/melee_weapons.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 3482028c1006..43b61866feef 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -247,11 +247,11 @@ name = "\improper AV-98 \"Ingram\" heavy stun baton" desc = "A stun baton, but bigger. The tide of toolbox-armed assistants don't stand a chance." energy_drain = 300 - attack_speed_modifier = 1.5 //needs to recharge + attack_speed_modifier = 2 //needs to recharge structure_damage_mult = 1 precise_weapon_damage = -20 //Mostly nonlethal weapon_damage = -20 - minimum_damage = 10 + minimum_damage = 10 hit_effect = ATTACK_EFFECT_BOOP //Boop :) attack_sharpness = SHARP_NONE mob_strike_sound = 'sound/weapons/egloves.ogg' From 0acb54a81f5814022b569b7a79047658b9ac6df4 Mon Sep 17 00:00:00 2001 From: Mek Date: Fri, 3 Feb 2023 21:34:49 -0500 Subject: [PATCH 07/26] this might work --- code/game/mecha/equipment/weapons/melee_weapons.dm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 43b61866feef..f63739003024 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -135,7 +135,9 @@ for(var/i = 0 to 2) var/it_turn = 45*(1-i) var/turf/T = get_step(M,turn(chassis.dir, it_turn)) //+45, +0, and -45 will get the three front tiles + special_hit(T) for(var/atom/A in T.contents) + special_hit(A) if(isliving(A)) var/mob/living/L = A @@ -215,7 +217,7 @@ light_range = 5 light_color = LIGHT_COLOR_RED -/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/energy_axe/cleave_attack(A) +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/energy_axe/special_hit(A) if(istype(A, /turf/closed/wall)) //IT BREAKS WALLS TOO var/turf/closed/wall/W = A playsound(W, 'sound/weapons/blade1.ogg', 50) From 4f48681c0327dc2db1b06a53c9fcff65ed47c18c Mon Sep 17 00:00:00 2001 From: Mek Date: Fri, 3 Feb 2023 21:51:51 -0500 Subject: [PATCH 08/26] comment --- code/game/mecha/equipment/weapons/melee_weapons.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index f63739003024..cbc0dc9abfa5 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -135,7 +135,7 @@ for(var/i = 0 to 2) var/it_turn = 45*(1-i) var/turf/T = get_step(M,turn(chassis.dir, it_turn)) //+45, +0, and -45 will get the three front tiles - special_hit(T) + special_hit(T) //So we can hit turfs too for(var/atom/A in T.contents) special_hit(A) if(isliving(A)) From b99f8011cdc3cdf8fe058304613f4a31d59d3f9e Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 4 Feb 2023 01:23:45 -0500 Subject: [PATCH 09/26] plus rapier --- .../mecha/equipment/weapons/melee_weapons.dm | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index cbc0dc9abfa5..ac0b390efb3a 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -337,6 +337,56 @@ L.throw_at(throwtarget, 8, 2, src) //Get outta here! do_item_attack_animation(L, hit_effect) +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier + name = "\improper E9-V \"Sigrun\" rapier" + desc = "A remarkably thin blade for a weapon wielded by an exosuit, this rapier is the favorite of syndicate pilots that perfer finesse over brute force." + energy_drain = 40 + cleave = FALSE + base_armor_piercing = 25 //50 on precise attack + structure_damage_mult = 2 //Ever try to shank an engine block? + attack_sharpness = SHARP_POINTY + attack_sound = 'sound/weapons/rapierhit.ogg' + sword_wound_bonus = 10 //Stabby + var/stab_number = 2 //Stabby stabby + var/last_lunge = 0 + var/lunge_cd = 20 //1 second cooldown on the lunge attack + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier/precise_attack(atom/target) + for(var/i in 1 to stab_number) + special_hit(target) + if(isliving(target)) + var/mob/living/L = target + + if(iscarbon(L)) + var/mob/living/carbon/C = L + var/obj/item/bodypart/body_part = C.get_bodypart(chassis.occupant? chassis.occupant.zone_selected : BODY_ZONE_CHEST) + if(i > 1) + body_part = pick(C.bodyparts) //If it's not the first hit we pick a random one + var/armor_block = C.run_armor_check(body_part, MELEE, armour_penetration = base_armor_piercing * 2) //more AP for precision attacks + C.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness, wound_bonus = sword_wound_bonus) + else + L.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type) + if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) //Stab them harder + L.apply_damage(fauna_damage_bonus, dam_type) + + L.visible_message(span_danger("[chassis.name] strikes [L] with [src]!"), \ + span_userdanger("[chassis.name] strikes you with [src]!")) + chassis.log_message("Hit [L] with [src.name] (precise attack).", LOG_MECHA) + + else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha)) //If the initial target is a big object, hit it even if it's not dense. + var/obj/O = target + var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult + O.take_damage(object_damage, dam_type, "melee", 0) + else + return + chassis.do_attack_animation(target, hit_effect) + playsound(chassis, attack_sound, 50, 1) + sleep(0.2 SECONDS) //For a slight delay between attacks + +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier/special_hit(atom/target) + if((world.time + lunge_cd) > last_lunge) + step_towards(src.chassis, target) + // //=========================================================\\ //======|| SNOWFLAKE WEAPONS THAT ARENT SWORDS || From bcc6d6bf4d97f2e35ea457867f628688ef9f3f26 Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 4 Feb 2023 02:22:49 -0500 Subject: [PATCH 10/26] yeah that works maybe --- .../mecha/equipment/weapons/melee_weapons.dm | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index ac0b390efb3a..56748a1f0500 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -286,7 +286,7 @@ H.apply_effect(EFFECT_STUTTER, stunforce) if(isliving(target)) - step_away(src, target) //We push all mobs back a tad + step_away(target, src) //We push all mobs back a tad /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/trogdor //TROGDOR!!!!! (But he's not a robot so I can't make the visible name that) @@ -312,7 +312,7 @@ C.adjustFireLoss(burn_damage) else L.apply_damage(burn_damage, BURN) - L.adjust_fire_stacks(5) + L.adjust_fire_stacks(2) L.IgniteMob() playsound(L, 'sound/items/welder.ogg', 50, 1) @@ -333,7 +333,7 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/maul/special_hit(atom/A) if(isliving(A)) var/mob/living/L = A - var/throwtarget = get_edge_target_turf(L, get_dir(L, get_step_away(src, L))) + var/throwtarget = get_edge_target_turf(src.chassis, get_dir(src, get_step_away(L, src))) L.throw_at(throwtarget, 8, 2, src) //Get outta here! do_item_attack_animation(L, hit_effect) @@ -345,14 +345,19 @@ base_armor_piercing = 25 //50 on precise attack structure_damage_mult = 2 //Ever try to shank an engine block? attack_sharpness = SHARP_POINTY + extended_range = 1 //so we can jump at people attack_sound = 'sound/weapons/rapierhit.ogg' sword_wound_bonus = 10 //Stabby var/stab_number = 2 //Stabby stabby - var/last_lunge = 0 - var/lunge_cd = 20 //1 second cooldown on the lunge attack + var/next_lunge = 0 + var/lunge_cd = 2 SECONDS //2 second cooldown on the lunge attack /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier/precise_attack(atom/target) + if(get_dist(get_turf(src.chassis), get_turf(target)) > 1) + return for(var/i in 1 to stab_number) + if(i == 1) + start_cooldown() //To avoid shenanigans because sleep is wonky special_hit(target) if(isliving(target)) var/mob/living/L = target @@ -384,8 +389,14 @@ sleep(0.2 SECONDS) //For a slight delay between attacks /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier/special_hit(atom/target) - if((world.time + lunge_cd) > last_lunge) - step_towards(src.chassis, target) + if(world.time < next_lunge) //On cooldown + return + if(get_dist(get_turf(src.chassis), get_turf(target)) <= 1) //Already in melee range + return + if(isturf(target)) //No free moving, you gotta stab something + return + step_towards(src.chassis, target) + next_lunge = world.time + lunge_cd // //=========================================================\\ @@ -415,13 +426,13 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/spear name = "\improper S5-C \"White Witch\" shortspear" desc = "A hardened, telescoping metal rod with a wicked-sharp tip. Perfect for punching holes in things normally out of reach." - icon_state = "mecha_spear" + //icon_state = "mecha_spear" get sprites!! energy_drain = 30 force = 10 //I want someone to stab someone else with this by hand extended_range = 1 //Hits from a tile away precise_weapon_damage = 10 minimum_damage = 20 - attack_speed_modifier = 1.2 //Slightly slower + attack_speed_modifier = 1.6 //Range comes with a cost attack_sharpness = SHARP_POINTY sharpness = SHARP_POINTY //You can't use it well but it IS still a giant sharp metal stick base_armor_piercing = 40 From 6cf9ec8b66245be15a3f5447a81fa7ae792a5b6c Mon Sep 17 00:00:00 2001 From: Mek Date: Mon, 6 Feb 2023 01:17:45 -0500 Subject: [PATCH 11/26] small tweaks that i did while spriting --- code/game/mecha/equipment/weapons/melee_weapons.dm | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 56748a1f0500..fbd176f690b9 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -235,14 +235,13 @@ name = "\improper OWM-5 \"Ronin\" katana" desc = "An oversized, light-weight replica of an ancient style of blade. Still woefully underpowered in D&D." energy_drain = 15 - precise_weapon_damage = 10 //noticeably less damage than its larger cousin cleave = FALSE //small fast blade attack_speed_modifier = 0.7 //live out your anime dreams in a mech fauna_damage_bonus = 20 //because why not - deflect_bonus = 15 - base_armor_piercing = 20 + deflect_bonus = 20 //anime reasons + base_armor_piercing = 15 //30 on the precise attacks structure_damage_mult = 1.5 //katana is less smashy than other swords - minimum_damage = 20 + minimum_damage = 15 sword_wound_bonus = 15 //More bleeding /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong From b192fd513f2e3e76ff121b322c9d308bbba425c5 Mon Sep 17 00:00:00 2001 From: Mek Date: Fri, 10 Feb 2023 05:58:20 -0500 Subject: [PATCH 12/26] holy shit its late --- .../mecha/equipment/weapons/melee_weapons.dm | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index fbd176f690b9..131a6e196447 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -68,17 +68,45 @@ return 0 if (targloc == curloc) return 0 - - if(target == targloc && !(chassis.occupant.a_intent == INTENT_HELP) && cleave) //If we are targetting a location, not an object or mob, and we're not in a passive stance cleave_attack() - else if(precise_attacks && (get_dist(src,target) <= (1 + extended_range))) //If we are targetting not a turf and they're within reach - precise_attack(target) //We stab it if we can + else if(precise_attacks && (get_dist(src,target) <= (1 + extended_range)) && can_stab_at(curloc, targloc)) //If we are targetting not a turf and they're within reach + precise_attack(target) //We stab it if we can else if(cleave) - cleave_attack() //Or swing wildly + cleave_attack() //Or swing wildly + else //Failure to sword + return 0 chassis.log_message("Attacked with [src.name], targeting [target].", LOG_MECHA) return 1 +/obj/item/mecha_parts/mecha_equipment/melee_weapon/proc/can_stab_at(turf/starter, turf/target) + //Note that we don't check for valid turfs or if starter and target are the same because that's already done in action() + var/turf/current = null + var/turf/next = null + if(abs(starter.x - target.x) == abs(starter.y - target.y)) //If we're exactly diagonal + current = get_step_towards(starter, target) //We check directly diagonal for reasons + else + current = get_step_towards2(starter, target) //Otherwise we use a more lenient check + if(!starter.Adjacent(current)) //Not next to our first turf, immediately fail + return 0 + if(current != target) //If our target isn't our first turf, find the next turf + next = get_step_towards(current, target) + while(current != target) //as long as we haven't reached our target turf + if(!current.Adjacent(next)) //check if we can stab from our current turf to the next one + return 0 + if(current.density) //If it's a wall and we're not at our target, we can't stab through it + return 0 + for(var/obj/O in current) + if(O.density && !(O.pass_flags & LETPASSTHROW)) //If there's a solid object we can't reach over on the turf + return 0 + current = next + if(next != target) //Move to the next tile if we're not already there + next = get_step_towards(next, target) + return 1 + + + + /obj/item/mecha_parts/mecha_equipment/melee_weapon/proc/precise_attack(atom/target) //No special attack by default. These will be set in the weapons themselves return 0 @@ -138,7 +166,7 @@ special_hit(T) //So we can hit turfs too for(var/atom/A in T.contents) special_hit(A) - if(isliving(A)) + if(isliving(A) && can_stab_at(M, T)) //If there's a stabbable mob var/mob/living/L = A if(iscarbon(L)) //If we're a carbon we can get armor and jazz @@ -156,7 +184,7 @@ chassis.log_message("Hit [L] with [src.name] (cleave attack).", LOG_MECHA) playsound(L, mob_strike_sound, 50) - else if(isstructure(A) || ismachinery(A) || istype(A, /obj/mecha)) //if it's something we can otherwise still hit + if(isstructure(A) || (ismachinery(A) && can_stab_at(M, T)) || (istype(A, /obj/mecha) && can_stab_at(M, T))) //if it's a big thing we hit anyways. Structures ALWAYS are hit, machines and mechs can be protected var/obj/O = A if(!O.density) //Make sure it's not an open door or something continue @@ -239,7 +267,7 @@ attack_speed_modifier = 0.7 //live out your anime dreams in a mech fauna_damage_bonus = 20 //because why not deflect_bonus = 20 //anime reasons - base_armor_piercing = 15 //30 on the precise attacks + base_armor_piercing = 10 //20 on the precise attacks, meant for lighter targets structure_damage_mult = 1.5 //katana is less smashy than other swords minimum_damage = 15 sword_wound_bonus = 15 //More bleeding @@ -352,7 +380,7 @@ var/lunge_cd = 2 SECONDS //2 second cooldown on the lunge attack /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier/precise_attack(atom/target) - if(get_dist(get_turf(src.chassis), get_turf(target)) > 1) + if(get_dist(get_turf(src.chassis), get_turf(target)) > 1) //Extended range except it has to be in melee to actually hit return for(var/i in 1 to stab_number) if(i == 1) @@ -385,6 +413,7 @@ return chassis.do_attack_animation(target, hit_effect) playsound(chassis, attack_sound, 50, 1) + start_cooldown() //no cheeky multistabbing sleep(0.2 SECONDS) //For a slight delay between attacks /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier/special_hit(atom/target) From 811bb3ec5796d46647c2d80a6817366c472a4127 Mon Sep 17 00:00:00 2001 From: Mek Date: Fri, 10 Feb 2023 23:14:52 -0500 Subject: [PATCH 13/26] rapier fix --- .../mecha/equipment/weapons/melee_weapons.dm | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 131a6e196447..1fbede9736b0 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -6,41 +6,41 @@ mech_flags = EXOSUIT_MODULE_COMBAT melee_override = TRUE var/restricted = TRUE //for our special hugbox exofabs - //If we have a longer range weapon, such as a spear or whatever capable of hitting people further away, this is how much extra range it has + /// If we have a longer range weapon, such as a spear or whatever capable of hitting people further away, this is how much extra range it has var/extended_range = 0 - //Attack speed modifier for a weapon. Big weapons will have a longer delay between attacks, while smaller ones will be faster + /// Attack speed modifier for a weapon. Big weapons will have a longer delay between attacks, while smaller ones will be faster var/attack_speed_modifier = 1 - //Attack sound for the weapon + /// Attack sound for the weapon var/attack_sound = 'sound/weapons/mechasword.ogg' //Attack types - Note that at least one of these must be true otherwise it'll only have passive effects (if any) //By default we assume we're using a small weapon with only a special single-target attack - //If the weapon has an AOE attack + /// If the weapon has an AOE attack var/cleave = FALSE - //If the weapon has a single-target strike + /// If the weapon has a single-target strike var/precise_attacks = TRUE - //Damage type for the weapon + /// Damage type for the weapon var/dam_type = BRUTE - //If it's sharp or not + /// If it's sharp or not var/attack_sharpness = SHARP_NONE - //Damage the weapon will do. Note this is ADDED to the base mecha attack damage (usually) + /// Damage the weapon will do. Note this is ADDED to the base mecha attack damage (usually) var/weapon_damage = 0 - //If we have both cleave and precise attacks, the precise may have more damage + /// If we have both cleave and precise attacks, the precise may have more damage var/precise_weapon_damage = 0 - //Minimum damage dealt with a weapon. Applies to non-combat mechs with the secret compartment module to make them suck a little less + /// Minimum damage dealt with a weapon. Applies to non-combat mechs with the secret compartment module to make them suck a little less var/minimum_damage = 0 - //Bonus deflection chance for using a melee weapon capable of blocking attacks + /// Bonus deflection chance for using a melee weapon capable of blocking attacks var/deflect_bonus = 0 - //Base armor piercing value of the weapon. This value is doubled for precise attacks + /// Base armor piercing value of the weapon. This value is doubled for precise attacks var/base_armor_piercing = 0 - //Fauna bonus damage, if any + /// Fauna bonus damage, if any var/fauna_damage_bonus = 0 - //Structure damage multiplier, for stuff like big ol' smashy hammers. Base structure damage multiplier for mech melee attacks is 3. + /// Structure damage multiplier, for stuff like big ol' smashy hammers. Base structure damage multiplier for mech melee attacks is 3. var/structure_damage_mult = 3 - //Effect on hitting something + /// Effect on hitting something var/hit_effect = ATTACK_EFFECT_SLASH - //Effect of the cleave attack + /// Effect of the cleave attack var/cleave_effect = /obj/effect/temp_visual/dir_setting/firing_effect/mecha_swipe /obj/item/mecha_parts/mecha_equipment/melee_weapon/can_attach(obj/mecha/M) @@ -372,19 +372,21 @@ base_armor_piercing = 25 //50 on precise attack structure_damage_mult = 2 //Ever try to shank an engine block? attack_sharpness = SHARP_POINTY + attack_speed_modifier = 0.8 //Counteracts the 0.2 second time between attacks extended_range = 1 //so we can jump at people attack_sound = 'sound/weapons/rapierhit.ogg' sword_wound_bonus = 10 //Stabby var/stab_number = 2 //Stabby stabby var/next_lunge = 0 - var/lunge_cd = 2 SECONDS //2 second cooldown on the lunge attack + var/base_lunge_cd = 1 //cooldown for lunge (in seconds because math) /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier/precise_attack(atom/target) - if(get_dist(get_turf(src.chassis), get_turf(target)) > 1) //Extended range except it has to be in melee to actually hit + if(get_dist(chassis, target) > 1) //First we hop forward + do_lunge_at(target) + if(get_dist(get_turf(src.chassis), get_turf(target)) > 1) //If we weren't able to get within range we don't attack + addtimer(CALLBACK(src, .proc/set_ready_state, 1, chassis.melee_cooldown * check_eva() * 0.5) ) //half cooldown on a failed lunge attack return for(var/i in 1 to stab_number) - if(i == 1) - start_cooldown() //To avoid shenanigans because sleep is wonky special_hit(target) if(isliving(target)) var/mob/living/L = target @@ -393,7 +395,7 @@ var/mob/living/carbon/C = L var/obj/item/bodypart/body_part = C.get_bodypart(chassis.occupant? chassis.occupant.zone_selected : BODY_ZONE_CHEST) if(i > 1) - body_part = pick(C.bodyparts) //If it's not the first hit we pick a random one + body_part = pick(C.bodyparts) //If it's not the first strike we pick a random one, mostly to reduce the chances of instant dismembering var/armor_block = C.run_armor_check(body_part, MELEE, armour_penetration = base_armor_piercing * 2) //more AP for precision attacks C.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness, wound_bonus = sword_wound_bonus) else @@ -413,10 +415,11 @@ return chassis.do_attack_animation(target, hit_effect) playsound(chassis, attack_sound, 50, 1) - start_cooldown() //no cheeky multistabbing - sleep(0.2 SECONDS) //For a slight delay between attacks + set_ready_state(0) //Wait till we're done multi-stabbing before we do it again + if(i != stab_number) //Only sleep between attacks + sleep(0.2 SECONDS) //Slight delay -/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier/special_hit(atom/target) +/obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier/proc/do_lunge_at(atom/target) if(world.time < next_lunge) //On cooldown return if(get_dist(get_turf(src.chassis), get_turf(target)) <= 1) //Already in melee range @@ -424,7 +427,7 @@ if(isturf(target)) //No free moving, you gotta stab something return step_towards(src.chassis, target) - next_lunge = world.time + lunge_cd + next_lunge = world.time + base_lunge_cd * (chassis.melee_cooldown * check_eva()) //If we can attack faster we can lunge faster too // //=========================================================\\ From aa63b03b4e035a7c2252c01d50d1fff0e4f4d08d Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 11 Feb 2023 02:50:32 -0500 Subject: [PATCH 14/26] sprite get --- .../mecha/equipment/weapons/melee_weapons.dm | 7 ++++++- icons/mecha/mecha_equipment.dmi | Bin 24544 -> 26875 bytes 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 1fbede9736b0..c16d758c025a 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -262,6 +262,7 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/katana //Anime mech sword name = "\improper OWM-5 \"Ronin\" katana" desc = "An oversized, light-weight replica of an ancient style of blade. Still woefully underpowered in D&D." + icon_state = "mecha_katana" energy_drain = 15 cleave = FALSE //small fast blade attack_speed_modifier = 0.7 //live out your anime dreams in a mech @@ -275,6 +276,7 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong name = "\improper AV-98 \"Ingram\" heavy stun baton" desc = "A stun baton, but bigger. The tide of toolbox-armed assistants don't stand a chance." + icon_state = "mecha_batong" energy_drain = 300 attack_speed_modifier = 2 //needs to recharge structure_damage_mult = 1 @@ -319,6 +321,7 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/trogdor //TROGDOR!!!!! (But he's not a robot so I can't make the visible name that) name = "\improper TO-4 \"Tahu\" flaming chainsword" //ITS ALSO A CHAINSWORD FUCK YEAH desc = "It's as ridiculous as it is badass. You feel like use of this this might be considered a war crime somewhere." + icon_state = "mecha_trogdor" energy_drain = 30 precise_weapon_damage = 5 //Gotta make space for the burninating attack_speed_modifier = 1.2 //Little unwieldy @@ -346,6 +349,7 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/maul name = "\improper CX-22 \"Barbados\" heavy maul" desc = "A massive, unwieldy, mace-like weapon, this thing really looks like something you don't want to be hit by if you're not a fan of being concave." + icon_state = "mecha_maul" energy_drain = 40 weapon_damage = 25 //Very smashy precise_weapon_damage = 30 @@ -367,6 +371,7 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier name = "\improper E9-V \"Sigrun\" rapier" desc = "A remarkably thin blade for a weapon wielded by an exosuit, this rapier is the favorite of syndicate pilots that perfer finesse over brute force." + icon_state = "mecha_rapier" energy_drain = 40 cleave = FALSE base_armor_piercing = 25 //50 on precise attack @@ -457,7 +462,7 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/spear name = "\improper S5-C \"White Witch\" shortspear" desc = "A hardened, telescoping metal rod with a wicked-sharp tip. Perfect for punching holes in things normally out of reach." - //icon_state = "mecha_spear" get sprites!! + icon_state = "mecha_spear" energy_drain = 30 force = 10 //I want someone to stab someone else with this by hand extended_range = 1 //Hits from a tile away diff --git a/icons/mecha/mecha_equipment.dmi b/icons/mecha/mecha_equipment.dmi index da630574358c4fd3f0e609e25f130481d2507540..68d41fb8457c62062970cbb9caac0f087e2fab52 100644 GIT binary patch literal 26875 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
N8XP+7qW8Y%P|6m#pqqTH$IkiEWLs}DO*g99 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 literal 24544 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

+9_PqvW>W_;8kMuoFBQqW&Q=1lnY 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 Date: Sat, 11 Feb 2023 03:28:17 -0500 Subject: [PATCH 15/26] its done i guess --- code/game/mecha/combat/gygax.dm | 4 +- .../mecha/equipment/weapons/melee_weapons.dm | 1 + .../modules/research/designs/mecha_designs.dm | 45 +++++++++++++++++++ code/modules/research/techweb/all_nodes.dm | 40 +++++++++++++++++ code/modules/research/techweb/layout.dm | 20 +++++++++ 5 files changed, 109 insertions(+), 1 deletion(-) diff --git a/code/game/mecha/combat/gygax.dm b/code/game/mecha/combat/gygax.dm index 1c6ab6eab258..b11ce02ea3da 100644 --- a/code/game/mecha/combat/gygax.dm +++ b/code/game/mecha/combat/gygax.dm @@ -26,7 +26,7 @@ operation_req_access = list(ACCESS_SYNDICATE) internals_req_access = list(ACCESS_SYNDICATE) wreckage = /obj/structure/mecha_wreckage/gygax/dark - max_equip = 4 + max_equip = 5 destruction_sleep_duration = 20 /obj/mecha/combat/gygax/dark/loaded/Initialize() @@ -35,6 +35,8 @@ ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/rapier + ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/teleporter ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index c16d758c025a..7aab9bc4200f 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -375,6 +375,7 @@ energy_drain = 40 cleave = FALSE base_armor_piercing = 25 //50 on precise attack + deflect_bonus = 15 //mech fencing but it parries bullets too because robot reaction time or something structure_damage_mult = 2 //Ever try to shank an engine block? attack_sharpness = SHARP_POINTY attack_speed_modifier = 0.8 //Counteracts the 0.2 second time between attacks diff --git a/code/modules/research/designs/mecha_designs.dm b/code/modules/research/designs/mecha_designs.dm index f3ae8cca2e0c..ddbfefdbcacb 100644 --- a/code/modules/research/designs/mecha_designs.dm +++ b/code/modules/research/designs/mecha_designs.dm @@ -524,3 +524,48 @@ materials = list(/datum/material/iron=15000,/datum/material/titanium=10000,/datum/material/plasma=10000) construction_time = 100 category = list("Exosuit Equipment") + +/datum/design/mech_katana + name = "Exosuit Weapon (OWM-5 \"Ronin\" katana)" + id = "mech_katana" + build_type = MECHFAB + build_path = /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/katana + materials = list(/datum/material/iron=15000,/datum/material/titanium=10000,/datum/material/bluespace=5000) + construction_time = 100 + category = list("Exosuit Equipment") + +/datum/design/mech_trogdor + name = "Exosuit Weapon (TO-4 \"Tahu\" flaming chainsword)" + id = "mech_trogdor" + build_type = MECHFAB + build_path = /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/trogdor + materials = list(/datum/material/iron=15000,/datum/material/silver=10000,/datum/material/gold=10000,/datum/material/plasma=10000 ) + construction_time = 100 + category = list("Exosuit Equipment") + +/datum/design/mech_stunbaton + name = "Exosuit Weapon (AV-98 \"Ingram\" heavy stun baton)" + id = "mech_stunbaton" + build_type = MECHFAB + build_path = /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong + materials = list(/datum/material/iron=15000,/datum/material/gold=10000,/datum/material/plasma=10000) + construction_time = 100 + category = list("Exosuit Equipment") + +/datum/design/mech_maul + name = "Exosuit Weapon (CX-22 \"Barbados\" heavy maul)" + id = "mech_maul" + build_type = MECHFAB + build_path = /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/maul + materials = list(/datum/material/iron=25000 ,/datum/material/uranium=10000) + construction_time = 100 + category = list("Exosuit Equipment") + +/datum/design/mech_spear + name = "Exosuit Weapon (S5-C \"White Witch\" shortspear)" + id = "mech_spear" + build_type = MECHFAB + build_path = /obj/item/mecha_parts/mecha_equipment/melee_weapon/spear + materials = list(/datum/material/iron=10000,/datum/material/titanium=10000,/datum/material/silver=10000) + construction_time = 100 + category = list("Exosuit Equipment") diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 154d04fde7cb..3f42841014f7 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -950,6 +950,46 @@ design_ids = list("mech_shortsword") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) +/datum/techweb_node/mech_katana + id = "mech_katana" + display_name = "Exosuit Weapon (OWM-5 \"Ronin\" katana)" + description = "An advanced piece of mech weaponry" + prereq_ids = list("adv_mecha","mech_shortsword") + design_ids = list("mech_katana") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + +/datum/techweb_node/mech_batong + id = "mech_batong" + display_name = "Exosuit Weapon (AV-98 \"Ingram\" heavy stun baton)" + description = "An advanced piece of mech weaponry" + prereq_ids = list("adv_mecha","adv_weaponry", "sec_basic") + design_ids = list("mech_batong") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + +/datum/techweb_node/mech_trogdor + id = "mech_trogdor" + display_name = "Exosuit Weapon (TO-4 \"Tahu\" flaming chainsword)" + description = "An advanced piece of mech weaponry" + prereq_ids = list("adv_mecha","mech_shortsword") + design_ids = list("mech_trogdor") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + +/datum/techweb_node/mech_maul + id = "mech_maul" + display_name = "Exosuit Weapon (CX-22 \"Barbados\" heavy maul)" + description = "An advanced piece of mech weaponry" + prereq_ids = list("adv_mecha","mech_shortsword") + design_ids = list("mech_maul") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + +/datum/techweb_node/mech_spear + id = "mech_spear" + display_name = "Exosuit Weapon (S5-C \"White Witch\" shortspear)" + description = "An advanced piece of mech weaponry" + prereq_ids = list("adv_mecha","adv_weaponry") + design_ids = list("mech_spear") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + /////////////////////////Nanites///////////////////////// /datum/techweb_node/nanite_base id = "nanite_base" diff --git a/code/modules/research/techweb/layout.dm b/code/modules/research/techweb/layout.dm index fe3a6dda35f8..e51b1622a195 100644 --- a/code/modules/research/techweb/layout.dm +++ b/code/modules/research/techweb/layout.dm @@ -615,6 +615,26 @@ ui_x = -640 ui_y = 32 +/datum/techweb_node/mech_katana + ui_x = -704 + ui_y = -32 + +/datum/techweb_node/mech_batong + ui_x = -512 + ui_y = -32 + +/datum/techweb_node/mech_trogdor + ui_x = -704 + ui_y = 32 + +/datum/techweb_node/mech_maul + ui_x = -704 + ui_y = -96 + +/datum/techweb_node/mech_spear + ui_x = -512 + ui_y = 32 + /datum/techweb_node/bshipc ui_x = -224 ui_y = 32 From 6e4e532eeca5b93423efaf102b164e312fe52107 Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 11 Feb 2023 03:48:14 -0500 Subject: [PATCH 16/26] last fixes --- code/game/mecha/equipment/weapons/melee_weapons.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 7aab9bc4200f..1ce98f78e682 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -70,7 +70,7 @@ return 0 if(target == targloc && !(chassis.occupant.a_intent == INTENT_HELP) && cleave) //If we are targetting a location, not an object or mob, and we're not in a passive stance cleave_attack() - else if(precise_attacks && (get_dist(src,target) <= (1 + extended_range)) && can_stab_at(curloc, targloc)) //If we are targetting not a turf and they're within reach + else if(precise_attacks && (get_dist(src,target) <= (1 + extended_range)) && can_stab_at(curloc, targloc) && !istype(target, obj/item) && !istype(target, obj/effect)) //If we are targetting something stabbable and they're within reach precise_attack(target) //We stab it if we can else if(cleave) cleave_attack() //Or swing wildly @@ -286,6 +286,7 @@ hit_effect = ATTACK_EFFECT_BOOP //Boop :) attack_sharpness = SHARP_NONE mob_strike_sound = 'sound/weapons/egloves.ogg' + attack_sound = 'sound/weapons/egloves.ogg' var/special_hit_stamina_damage = 80 //A bit stronger than a normal baton var/stunforce = 10 SECONDS From 9f77b6a79a281cb26cab5620a4d29fb22382f58b Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 11 Feb 2023 04:18:22 -0500 Subject: [PATCH 17/26] nodamage --- .../mecha/equipment/weapons/melee_weapons.dm | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 1ce98f78e682..aa5b232874be 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -70,7 +70,7 @@ return 0 if(target == targloc && !(chassis.occupant.a_intent == INTENT_HELP) && cleave) //If we are targetting a location, not an object or mob, and we're not in a passive stance cleave_attack() - else if(precise_attacks && (get_dist(src,target) <= (1 + extended_range)) && can_stab_at(curloc, targloc) && !istype(target, obj/item) && !istype(target, obj/effect)) //If we are targetting something stabbable and they're within reach + else if(precise_attacks && (get_dist(src,target) <= (1 + extended_range)) && can_stab_at(curloc, targloc) && !istype(target, /obj/item) && !istype(target, /obj/effect)) //If we are targetting something stabbable and they're within reach precise_attack(target) //We stab it if we can else if(cleave) cleave_attack() //Or swing wildly @@ -140,7 +140,8 @@ harmful = TRUE //DO NOT give to children. Or do, I'm not the police. minimum_damage = 0 var/sword_wound_bonus = 0 //Wound bonus if it's supposed to be ouchy beyond just doing damage - + var/precise_no_mobdamage = FALSE //If our precise attacks have a light touch for mobs + var/precise_no_objdamage = FALSE //Same but for objects/structures /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/special_hit(atom/target) return 0 @@ -205,12 +206,12 @@ if(isliving(target)) var/mob/living/L = target - if(iscarbon(L)) + if(iscarbon(L) && !precise_no_mobdamage) var/mob/living/carbon/C = L var/obj/item/bodypart/body_part = L.get_bodypart(chassis.occupant? chassis.occupant.zone_selected : BODY_ZONE_CHEST) var/armor_block = C.run_armor_check(body_part, MELEE, armour_penetration = base_armor_piercing * 2) //more AP for precision attacks C.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness, wound_bonus = sword_wound_bonus) - else + else if(!precise_no_mobdamage) L.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type) if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) //Stab them harder L.apply_damage(fauna_damage_bonus, dam_type) @@ -219,7 +220,7 @@ span_userdanger("[chassis.name] strikes you with [src]!")) chassis.log_message("Hit [L] with [src.name] (precise attack).", LOG_MECHA) - else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha)) //If the initial target is a big object, hit it even if it's not dense. + else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha) && !precise_no_objdamage) //If the initial target is a big object, hit it even if it's not dense. var/obj/O = target var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult O.take_damage(object_damage, dam_type, "melee", 0) @@ -283,12 +284,13 @@ precise_weapon_damage = -20 //Mostly nonlethal weapon_damage = -20 minimum_damage = 10 - hit_effect = ATTACK_EFFECT_BOOP //Boop :) + hit_effect = ATTACK_EFFECT_BOOP //Boop :^) attack_sharpness = SHARP_NONE + precise_no_mobdamage = TRUE //Light touch for targetted stuns mob_strike_sound = 'sound/weapons/egloves.ogg' attack_sound = 'sound/weapons/egloves.ogg' var/special_hit_stamina_damage = 80 //A bit stronger than a normal baton - var/stunforce = 10 SECONDS + var/stunforce = 12 SECONDS //Stuns a little harder too /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong/special_hit(atom/target) //It's a stun baton. It stuns. if(ishuman(target)) @@ -398,14 +400,14 @@ if(isliving(target)) var/mob/living/L = target - if(iscarbon(L)) + if(iscarbon(L) && !precise_no_mobdamage) var/mob/living/carbon/C = L var/obj/item/bodypart/body_part = C.get_bodypart(chassis.occupant? chassis.occupant.zone_selected : BODY_ZONE_CHEST) if(i > 1) body_part = pick(C.bodyparts) //If it's not the first strike we pick a random one, mostly to reduce the chances of instant dismembering var/armor_block = C.run_armor_check(body_part, MELEE, armour_penetration = base_armor_piercing * 2) //more AP for precision attacks C.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type, body_part, armor_block, sharpness = attack_sharpness, wound_bonus = sword_wound_bonus) - else + else if(!precise_no_mobdamage) L.apply_damage(max(chassis.force + precise_weapon_damage, minimum_damage), dam_type) if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) //Stab them harder L.apply_damage(fauna_damage_bonus, dam_type) @@ -414,7 +416,7 @@ span_userdanger("[chassis.name] strikes you with [src]!")) chassis.log_message("Hit [L] with [src.name] (precise attack).", LOG_MECHA) - else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha)) //If the initial target is a big object, hit it even if it's not dense. + else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha) && !precise_no_objdamage) //If the initial target is a big object, hit it even if it's not dense. var/obj/O = target var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult O.take_damage(object_damage, dam_type, "melee", 0) From 57df25ad26d26603be631581b022d632bd913144 Mon Sep 17 00:00:00 2001 From: Mek Date: Thu, 23 Feb 2023 05:13:46 -0500 Subject: [PATCH 18/26] this was supposed to not be a bug fix --- .../mecha/equipment/weapons/melee_weapons.dm | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index aa5b232874be..268f50cabe70 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -70,7 +70,7 @@ return 0 if(target == targloc && !(chassis.occupant.a_intent == INTENT_HELP) && cleave) //If we are targetting a location, not an object or mob, and we're not in a passive stance cleave_attack() - else if(precise_attacks && (get_dist(src,target) <= (1 + extended_range)) && can_stab_at(curloc, targloc) && !istype(target, /obj/item) && !istype(target, /obj/effect)) //If we are targetting something stabbable and they're within reach + else if(precise_attacks && (get_dist(src,target) <= (1 + extended_range)) && can_stab_at(chassis, target) && !istype(target, /obj/item) && !istype(target, /obj/effect)) //If we are targetting something stabbable and they're within reach precise_attack(target) //We stab it if we can else if(cleave) cleave_attack() //Or swing wildly @@ -79,19 +79,24 @@ chassis.log_message("Attacked with [src.name], targeting [target].", LOG_MECHA) return 1 -/obj/item/mecha_parts/mecha_equipment/melee_weapon/proc/can_stab_at(turf/starter, turf/target) +/obj/item/mecha_parts/mecha_equipment/melee_weapon/proc/can_stab_at(atom/user, atom/target) //Note that we don't check for valid turfs or if starter and target are the same because that's already done in action() var/turf/current = null var/turf/next = null - if(abs(starter.x - target.x) == abs(starter.y - target.y)) //If we're exactly diagonal - current = get_step_towards(starter, target) //We check directly diagonal for reasons + var/turf/starter = get_turf(user) + var/turf/targloc = get_turf(target) + + if(user.Adjacent(target)) //Check if we're in normal melee range. If we aren't the rest of this will run to check if we have line of stab. + return 1 + if(abs(starter.x - targloc.x) == abs(starter.y - targloc.y)) //If we're exactly diagonal + current = get_step_towards(starter, targloc) //We check directly diagonal for reasons else - current = get_step_towards2(starter, target) //Otherwise we use a more lenient check + current = get_step_towards2(starter, targloc) //Otherwise we use a more lenient check if(!starter.Adjacent(current)) //Not next to our first turf, immediately fail return 0 - if(current != target) //If our target isn't our first turf, find the next turf - next = get_step_towards(current, target) - while(current != target) //as long as we haven't reached our target turf + if(current != targloc) //If our target isn't our first turf, find the next turf + next = get_step_towards(current, targloc) + while(current != targloc) //as long as we haven't reached our target turf if(!current.Adjacent(next)) //check if we can stab from our current turf to the next one return 0 if(current.density) //If it's a wall and we're not at our target, we can't stab through it @@ -100,8 +105,8 @@ if(O.density && !(O.pass_flags & LETPASSTHROW)) //If there's a solid object we can't reach over on the turf return 0 current = next - if(next != target) //Move to the next tile if we're not already there - next = get_step_towards(next, target) + if(next != targloc) //Move to the next tile if we're not already there + next = get_step_towards(next, targloc) return 1 @@ -448,12 +453,11 @@ name = "\improper DD-2 \"Atom Smasher\" rocket fist" desc = "A large metal fist fitted to the arm of an exosuit, it uses repurposed maneuvering thrusters from a Raven battlecruiser to give a little more oomph to every punch. Also helps increase the speed at which the mech is able to return to a ready stance after each swing." icon_state = "mecha_rocket_fist" - melee_override = FALSE //We'll just buff the regular punch - precise_attacks = FALSE - cleave = FALSE - range = null //This should just make the mech punch stuff, which is what we want! weapon_damage = 20 +/obj/item/mecha_parts/mecha_equipment/melee_weapon/rocket_fist/precise_attack(atom/target) + target.mech_melee_attack(chassis, FALSE) //DONT SET THIS TO TRUE + /obj/item/mecha_parts/mecha_equipment/melee_weapon/rocket_fist/on_select() chassis.force += weapon_damage //PUNCH HARDER chassis.melee_cooldown *= 0.8 //PUNCH FASTER From d0899841554a9825e37e514cc70816f61a7e1cb6 Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 25 Feb 2023 04:29:46 -0500 Subject: [PATCH 19/26] weapon tweaks --- .../mecha/equipment/weapons/melee_weapons.dm | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 268f50cabe70..08de966ea990 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -155,8 +155,8 @@ name = "\improper GD6 \"Jaeger\" shortsword" desc = "An extendable arm-mounted blade with a nasty edge. It is small and fast enough to deflect some incoming attacks." energy_drain = 20 - weapon_damage = 15 - precise_weapon_damage = 20 + weapon_damage = 10 + precise_weapon_damage = 15 fauna_damage_bonus = 30 //because why not deflect_bonus = 15 base_armor_piercing = 15 @@ -194,7 +194,7 @@ var/obj/O = A if(!O.density) //Make sure it's not an open door or something continue - var/object_damage = max(chassis.force + weapon_damage, minimum_damage) * structure_damage_mult + var/object_damage = max(chassis.force + weapon_damage, minimum_damage) * structure_damage_mult * (istype(target, /obj/mecha) ? 0.5 : 1) //Half damage on mechs O.take_damage(object_damage, dam_type, "melee", 0) if(istype(O, /obj/structure/window)) playsound(O,'sound/effects/Glasshit.ogg', 50) //glass bonk noise @@ -227,7 +227,7 @@ else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha) && !precise_no_objdamage) //If the initial target is a big object, hit it even if it's not dense. var/obj/O = target - var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult + var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult * (istype(target, /obj/mecha) ? 0.5 : 1) //Half damage on mechs to prolong COOL MECH FIGHTS O.take_damage(object_damage, dam_type, "melee", 0) else return @@ -271,12 +271,13 @@ icon_state = "mecha_katana" energy_drain = 15 cleave = FALSE //small fast blade + precise_weapon_damage = 5 attack_speed_modifier = 0.7 //live out your anime dreams in a mech fauna_damage_bonus = 20 //because why not - deflect_bonus = 20 //anime reasons + deflect_bonus = 20 //ANIME REASONS base_armor_piercing = 10 //20 on the precise attacks, meant for lighter targets - structure_damage_mult = 1.5 //katana is less smashy than other swords - minimum_damage = 15 + structure_damage_mult = 2 //katana is less smashy than other swords + minimum_damage = 20 sword_wound_bonus = 15 //More bleeding /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong @@ -286,15 +287,15 @@ energy_drain = 300 attack_speed_modifier = 2 //needs to recharge structure_damage_mult = 1 - precise_weapon_damage = -20 //Mostly nonlethal - weapon_damage = -20 + precise_weapon_damage = -25 //Mostly nonlethal + weapon_damage = -25 minimum_damage = 10 hit_effect = ATTACK_EFFECT_BOOP //Boop :^) attack_sharpness = SHARP_NONE precise_no_mobdamage = TRUE //Light touch for targetted stuns mob_strike_sound = 'sound/weapons/egloves.ogg' attack_sound = 'sound/weapons/egloves.ogg' - var/special_hit_stamina_damage = 80 //A bit stronger than a normal baton + var/special_hit_stamina_damage = 75 //A bit stronger than a normal baton var/stunforce = 12 SECONDS //Stuns a little harder too /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/batong/special_hit(atom/target) //It's a stun baton. It stuns. @@ -337,7 +338,7 @@ base_armor_piercing = 15 structure_damage_mult = 3.5 //It melts AND cuts! sword_wound_bonus = -30 //We're here for the fire damage thank you - var/burninating = 15 //BURNINATING THE COUNTRYSIDE + var/burninating = 10 //BURNINATING THE COUNTRYSIDE mob_strike_sound = 'sound/weapons/chainsawhit.ogg' /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/trogdor/special_hit(atom/A) @@ -361,7 +362,7 @@ energy_drain = 40 weapon_damage = 25 //Very smashy precise_weapon_damage = 30 - attack_speed_modifier = 2 //Very slow + attack_speed_modifier = 2.5 //Very slow fauna_damage_bonus = 40 structure_damage_mult = 4 //Good for stationary objects attack_sharpness = SHARP_NONE @@ -506,7 +507,7 @@ else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha)) //If the initial target is a big object, hit it even if it's not dense. var/obj/O = target - var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult + var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult //It's anti-mech and the multiplier is already pretty low O.take_damage(object_damage, dam_type, "melee", 0) if(istype(target, /obj/mecha)) special_hit(target) From 4fa95aced37810fc1a8bc2663d62c477dceb4dde Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 25 Feb 2023 04:52:04 -0500 Subject: [PATCH 20/26] more tweaks n changes n variables --- .../mecha/equipment/weapons/melee_weapons.dm | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 08de966ea990..044ead1ef120 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -37,6 +37,8 @@ var/fauna_damage_bonus = 0 /// Structure damage multiplier, for stuff like big ol' smashy hammers. Base structure damage multiplier for mech melee attacks is 3. var/structure_damage_mult = 3 + /// Mech damage multiplier, modifies the structure damage multiplier for damage specifically against mechs. Default to 0.5 for extended mech combat gaming + var/mech_damage_multiplier = 0.6 /// Effect on hitting something var/hit_effect = ATTACK_EFFECT_SLASH @@ -79,14 +81,14 @@ chassis.log_message("Attacked with [src.name], targeting [target].", LOG_MECHA) return 1 -/obj/item/mecha_parts/mecha_equipment/melee_weapon/proc/can_stab_at(atom/user, atom/target) +/obj/item/mecha_parts/mecha_equipment/melee_weapon/proc/can_stab_at(atom/attacker, atom/target) //Checks for line of stab (like line of sight but harder) from attacker to defender //Note that we don't check for valid turfs or if starter and target are the same because that's already done in action() var/turf/current = null var/turf/next = null - var/turf/starter = get_turf(user) + var/turf/starter = get_turf(attacker) var/turf/targloc = get_turf(target) - if(user.Adjacent(target)) //Check if we're in normal melee range. If we aren't the rest of this will run to check if we have line of stab. + if(attacker.Adjacent(target)) //Check if we're in normal melee range. If we aren't the rest of this will run to check if we have line of stab. return 1 if(abs(starter.x - targloc.x) == abs(starter.y - targloc.y)) //If we're exactly diagonal current = get_step_towards(starter, targloc) //We check directly diagonal for reasons @@ -172,7 +174,7 @@ special_hit(T) //So we can hit turfs too for(var/atom/A in T.contents) special_hit(A) - if(isliving(A) && can_stab_at(M, T)) //If there's a stabbable mob + if(isliving(A) && can_stab_at(chassis, A)) //If there's a stabbable mob var/mob/living/L = A if(iscarbon(L)) //If we're a carbon we can get armor and jazz @@ -190,12 +192,13 @@ chassis.log_message("Hit [L] with [src.name] (cleave attack).", LOG_MECHA) playsound(L, mob_strike_sound, 50) - if(isstructure(A) || (ismachinery(A) && can_stab_at(M, T)) || (istype(A, /obj/mecha) && can_stab_at(M, T))) //if it's a big thing we hit anyways. Structures ALWAYS are hit, machines and mechs can be protected + if((isstructure(A) || ismachinery(A) || istype(A, /obj/mecha)) && can_stab_at(chassis, A)) //if it's a big thing we hit anyways. Structures ALWAYS are hit, machines and mechs can be protected var/obj/O = A if(!O.density) //Make sure it's not an open door or something continue - var/object_damage = max(chassis.force + weapon_damage, minimum_damage) * structure_damage_mult * (istype(target, /obj/mecha) ? 0.5 : 1) //Half damage on mechs - O.take_damage(object_damage, dam_type, "melee", 0) + var/object_damage = max(chassis.force + weapon_damage, minimum_damage) * structure_damage_mult * (istype(A, /obj/mecha) ? mech_damage_multiplier : 1) //Half damage on mechs + var/final_AP = istype(A, /obj/mecha) ? base_armor_piercing : 0 + O.take_damage(object_damage, dam_type, "melee", armour_penetration = final_AP) if(istype(O, /obj/structure/window)) playsound(O,'sound/effects/Glasshit.ogg', 50) //glass bonk noise else @@ -227,8 +230,9 @@ else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha) && !precise_no_objdamage) //If the initial target is a big object, hit it even if it's not dense. var/obj/O = target - var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult * (istype(target, /obj/mecha) ? 0.5 : 1) //Half damage on mechs to prolong COOL MECH FIGHTS - O.take_damage(object_damage, dam_type, "melee", 0) + var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult * (istype(target, /obj/mecha) ? mech_damage_multiplier : 1) //Half damage on mechs to prolong COOL MECH FIGHTS + var/final_AP = istype(target, /obj/mecha) ? base_armor_piercing * 2 : 0 + O.take_damage(object_damage, dam_type, "melee", armour_penetration = final_AP) else return chassis.do_attack_animation(target, hit_effect) @@ -386,6 +390,7 @@ base_armor_piercing = 25 //50 on precise attack deflect_bonus = 15 //mech fencing but it parries bullets too because robot reaction time or something structure_damage_mult = 2 //Ever try to shank an engine block? + mech_damage_multiplier = 0.75 //Slightly better against mechs attack_sharpness = SHARP_POINTY attack_speed_modifier = 0.8 //Counteracts the 0.2 second time between attacks extended_range = 1 //so we can jump at people @@ -424,8 +429,9 @@ else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha) && !precise_no_objdamage) //If the initial target is a big object, hit it even if it's not dense. var/obj/O = target - var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult - O.take_damage(object_damage, dam_type, "melee", 0) + var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult * (istype(target, /obj/mecha) ? mech_damage_multiplier : 1) //Nukie mech, slightly less bad at killing mechs + var/final_AP = istype(target, /obj/mecha) ? base_armor_piercing * 2 : 0 + O.take_damage(object_damage, dam_type, "melee", armour_penetration = final_AP) else return chassis.do_attack_animation(target, hit_effect) @@ -482,6 +488,7 @@ sharpness = SHARP_POINTY //You can't use it well but it IS still a giant sharp metal stick base_armor_piercing = 40 structure_damage_mult = 1.5 //Not great at destroying stuff + mech_damage_multiplier = 1 //Anti-mech weapon hit_effect = ATTACK_EFFECT_KICK //Don't question it @@ -507,8 +514,9 @@ else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha)) //If the initial target is a big object, hit it even if it's not dense. var/obj/O = target - var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult //It's anti-mech and the multiplier is already pretty low - O.take_damage(object_damage, dam_type, "melee", 0) + var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult * (istype(target, /obj/mecha) ? mech_damage_multiplier : 1) + var/final_AP = istype(target, /obj/mecha) ? base_armor_piercing : 0 //Flat AP here because the spear doesn't use normal double scaling for precise attacks + O.take_damage(object_damage, dam_type, "melee", armour_penetration = final_AP) if(istype(target, /obj/mecha)) special_hit(target) else From c6f52fb2448e409956f707caf96d829896999794 Mon Sep 17 00:00:00 2001 From: Mek Date: Sat, 25 Feb 2023 05:13:22 -0500 Subject: [PATCH 21/26] FUCK armour penetration all my homies hate object armor --- .../mecha/equipment/weapons/melee_weapons.dm | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 044ead1ef120..cda9d790e9c2 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -37,8 +37,8 @@ var/fauna_damage_bonus = 0 /// Structure damage multiplier, for stuff like big ol' smashy hammers. Base structure damage multiplier for mech melee attacks is 3. var/structure_damage_mult = 3 - /// Mech damage multiplier, modifies the structure damage multiplier for damage specifically against mechs. Default to 0.5 for extended mech combat gaming - var/mech_damage_multiplier = 0.6 + /// Mech damage multiplier, modifies the structure damage multiplier for damage specifically against mechs. Default to 0.75 for extended mech combat gaming + var/mech_damage_multiplier = 0.75 /// Effect on hitting something var/hit_effect = ATTACK_EFFECT_SLASH @@ -197,8 +197,7 @@ if(!O.density) //Make sure it's not an open door or something continue var/object_damage = max(chassis.force + weapon_damage, minimum_damage) * structure_damage_mult * (istype(A, /obj/mecha) ? mech_damage_multiplier : 1) //Half damage on mechs - var/final_AP = istype(A, /obj/mecha) ? base_armor_piercing : 0 - O.take_damage(object_damage, dam_type, "melee", armour_penetration = final_AP) + O.take_damage(object_damage, dam_type, "melee", 0) if(istype(O, /obj/structure/window)) playsound(O,'sound/effects/Glasshit.ogg', 50) //glass bonk noise else @@ -231,8 +230,7 @@ else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha) && !precise_no_objdamage) //If the initial target is a big object, hit it even if it's not dense. var/obj/O = target var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult * (istype(target, /obj/mecha) ? mech_damage_multiplier : 1) //Half damage on mechs to prolong COOL MECH FIGHTS - var/final_AP = istype(target, /obj/mecha) ? base_armor_piercing * 2 : 0 - O.take_damage(object_damage, dam_type, "melee", armour_penetration = final_AP) + O.take_damage(object_damage, dam_type, "melee", 0) else return chassis.do_attack_animation(target, hit_effect) @@ -390,7 +388,7 @@ base_armor_piercing = 25 //50 on precise attack deflect_bonus = 15 //mech fencing but it parries bullets too because robot reaction time or something structure_damage_mult = 2 //Ever try to shank an engine block? - mech_damage_multiplier = 0.75 //Slightly better against mechs + mech_damage_multiplier = 0.85 //Slightly better against mechs attack_sharpness = SHARP_POINTY attack_speed_modifier = 0.8 //Counteracts the 0.2 second time between attacks extended_range = 1 //so we can jump at people @@ -430,8 +428,7 @@ else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha) && !precise_no_objdamage) //If the initial target is a big object, hit it even if it's not dense. var/obj/O = target var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult * (istype(target, /obj/mecha) ? mech_damage_multiplier : 1) //Nukie mech, slightly less bad at killing mechs - var/final_AP = istype(target, /obj/mecha) ? base_armor_piercing * 2 : 0 - O.take_damage(object_damage, dam_type, "melee", armour_penetration = final_AP) + O.take_damage(object_damage, dam_type, "melee", 0) else return chassis.do_attack_animation(target, hit_effect) @@ -515,8 +512,7 @@ else if(isstructure(target) || ismachinery(target) || istype(target, /obj/mecha)) //If the initial target is a big object, hit it even if it's not dense. var/obj/O = target var/object_damage = max(chassis.force + precise_weapon_damage, minimum_damage) * structure_damage_mult * (istype(target, /obj/mecha) ? mech_damage_multiplier : 1) - var/final_AP = istype(target, /obj/mecha) ? base_armor_piercing : 0 //Flat AP here because the spear doesn't use normal double scaling for precise attacks - O.take_damage(object_damage, dam_type, "melee", armour_penetration = final_AP) + O.take_damage(object_damage, dam_type, "melee", 0) if(istype(target, /obj/mecha)) special_hit(target) else From fc6c2c234477963744d795dbadf0891eb3834acb Mon Sep 17 00:00:00 2001 From: Mqiib <43766432+Mqiib@users.noreply.github.com> Date: Sun, 26 Feb 2023 00:29:26 -0500 Subject: [PATCH 22/26] small island country --- code/game/mecha/equipment/weapons/melee_weapons.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index cda9d790e9c2..237d4a49a96e 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -358,7 +358,7 @@ playsound(L, 'sound/items/welder.ogg', 50, 1) /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/maul - name = "\improper CX-22 \"Barbados\" heavy maul" + name = "\improper CX-22 \"Barbatos\" heavy maul" desc = "A massive, unwieldy, mace-like weapon, this thing really looks like something you don't want to be hit by if you're not a fan of being concave." icon_state = "mecha_maul" energy_drain = 40 From a08a11b7cba297f1660cecbfe62019880c240173 Mon Sep 17 00:00:00 2001 From: Mqiib <43766432+Mqiib@users.noreply.github.com> Date: Sun, 26 Feb 2023 00:32:00 -0500 Subject: [PATCH 23/26] SMALL ISLAND COUNTRY --- code/modules/research/designs/mecha_designs.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/research/designs/mecha_designs.dm b/code/modules/research/designs/mecha_designs.dm index ddbfefdbcacb..3cf59ff5829d 100644 --- a/code/modules/research/designs/mecha_designs.dm +++ b/code/modules/research/designs/mecha_designs.dm @@ -553,7 +553,7 @@ category = list("Exosuit Equipment") /datum/design/mech_maul - name = "Exosuit Weapon (CX-22 \"Barbados\" heavy maul)" + name = "Exosuit Weapon (CX-22 \"Barbatos\" heavy maul)" id = "mech_maul" build_type = MECHFAB build_path = /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/maul From d51d2a6b2127d001b37b9389edab2caa6a37a019 Mon Sep 17 00:00:00 2001 From: Mqiib <43766432+Mqiib@users.noreply.github.com> Date: Sun, 26 Feb 2023 00:33:09 -0500 Subject: [PATCH 24/26] SM A L L IS L AND CO UN T RY --- code/modules/research/techweb/all_nodes.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 3f42841014f7..6a92185e5d7d 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -976,7 +976,7 @@ /datum/techweb_node/mech_maul id = "mech_maul" - display_name = "Exosuit Weapon (CX-22 \"Barbados\" heavy maul)" + display_name = "Exosuit Weapon (CX-22 \"Barbatos\" heavy maul)" description = "An advanced piece of mech weaponry" prereq_ids = list("adv_mecha","mech_shortsword") design_ids = list("mech_maul") From bd13a331f9ec330297764333dc5b852f10b58326 Mon Sep 17 00:00:00 2001 From: Mek Date: Fri, 3 Mar 2023 03:13:49 -0500 Subject: [PATCH 25/26] combat design --- code/modules/research/designs/mecha_designs.dm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/modules/research/designs/mecha_designs.dm b/code/modules/research/designs/mecha_designs.dm index fd63a2dc7fcf..9542fc1277b2 100644 --- a/code/modules/research/designs/mecha_designs.dm +++ b/code/modules/research/designs/mecha_designs.dm @@ -559,6 +559,7 @@ materials = list(/datum/material/iron=15000,/datum/material/titanium=10000,/datum/material/bluespace=5000) construction_time = 100 category = list("Exosuit Equipment") + combat_design = TRUE /datum/design/mech_trogdor name = "Exosuit Weapon (TO-4 \"Tahu\" flaming chainsword)" @@ -568,6 +569,7 @@ materials = list(/datum/material/iron=15000,/datum/material/silver=10000,/datum/material/gold=10000,/datum/material/plasma=10000 ) construction_time = 100 category = list("Exosuit Equipment") + combat_design = TRUE /datum/design/mech_stunbaton name = "Exosuit Weapon (AV-98 \"Ingram\" heavy stun baton)" @@ -577,6 +579,7 @@ materials = list(/datum/material/iron=15000,/datum/material/gold=10000,/datum/material/plasma=10000) construction_time = 100 category = list("Exosuit Equipment") + combat_design = TRUE /datum/design/mech_maul name = "Exosuit Weapon (CX-22 \"Barbados\" heavy maul)" @@ -586,6 +589,7 @@ materials = list(/datum/material/iron=25000 ,/datum/material/uranium=10000) construction_time = 100 category = list("Exosuit Equipment") + combat_design = TRUE /datum/design/mech_spear name = "Exosuit Weapon (S5-C \"White Witch\" shortspear)" @@ -595,3 +599,4 @@ materials = list(/datum/material/iron=10000,/datum/material/titanium=10000,/datum/material/silver=10000) construction_time = 100 category = list("Exosuit Equipment") + combat_design = TRUE From 391ba3ddc1852e3ed23153335c5ed469186d3f7f Mon Sep 17 00:00:00 2001 From: Mek Date: Fri, 3 Mar 2023 23:06:22 -0500 Subject: [PATCH 26/26] turf stabbing --- .../mecha/equipment/weapons/melee_weapons.dm | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index 237d4a49a96e..b894fa6168c9 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -6,43 +6,45 @@ mech_flags = EXOSUIT_MODULE_COMBAT melee_override = TRUE var/restricted = TRUE //for our special hugbox exofabs - /// If we have a longer range weapon, such as a spear or whatever capable of hitting people further away, this is how much extra range it has + /// If we have a longer range weapon, such as a spear or whatever capable of hitting people further away, this is how much extra range it has var/extended_range = 0 - /// Attack speed modifier for a weapon. Big weapons will have a longer delay between attacks, while smaller ones will be faster + /// Attack speed modifier for a weapon. Big weapons will have a longer delay between attacks, while smaller ones will be faster var/attack_speed_modifier = 1 - /// Attack sound for the weapon + /// Attack sound for the weapon var/attack_sound = 'sound/weapons/mechasword.ogg' //Attack types - Note that at least one of these must be true otherwise it'll only have passive effects (if any) //By default we assume we're using a small weapon with only a special single-target attack - /// If the weapon has an AOE attack + /// If the weapon has an AOE attack var/cleave = FALSE - /// If the weapon has a single-target strike + /// If the weapon has a single-target strike var/precise_attacks = TRUE - /// Damage type for the weapon + /// Damage type for the weapon var/dam_type = BRUTE - /// If it's sharp or not + /// If it's sharp or not var/attack_sharpness = SHARP_NONE - /// Damage the weapon will do. Note this is ADDED to the base mecha attack damage (usually) + /// Damage the weapon will do. Note this is ADDED to the base mecha attack damage (usually) var/weapon_damage = 0 - /// If we have both cleave and precise attacks, the precise may have more damage + /// If we have both cleave and precise attacks, the precise may have more damage var/precise_weapon_damage = 0 - /// Minimum damage dealt with a weapon. Applies to non-combat mechs with the secret compartment module to make them suck a little less + /// Minimum damage dealt with a weapon. Applies to non-combat mechs with the secret compartment module to make them suck a little less var/minimum_damage = 0 - /// Bonus deflection chance for using a melee weapon capable of blocking attacks + /// Bonus deflection chance for using a melee weapon capable of blocking attacks var/deflect_bonus = 0 - /// Base armor piercing value of the weapon. This value is doubled for precise attacks + /// Base armor piercing value of the weapon. This value is doubled for precise attacks var/base_armor_piercing = 0 - /// Fauna bonus damage, if any + /// Fauna bonus damage, if any var/fauna_damage_bonus = 0 - /// Structure damage multiplier, for stuff like big ol' smashy hammers. Base structure damage multiplier for mech melee attacks is 3. + /// Structure damage multiplier, for stuff like big ol' smashy hammers. Base structure damage multiplier for mech melee attacks is 3. var/structure_damage_mult = 3 - /// Mech damage multiplier, modifies the structure damage multiplier for damage specifically against mechs. Default to 0.75 for extended mech combat gaming + /// Mech damage multiplier, modifies the structure damage multiplier for damage specifically against mechs. Default to 0.75 for extended mech combat gaming var/mech_damage_multiplier = 0.75 - - /// Effect on hitting something + /// Weapons that can hit turfs, default to false because it'll be special effects maybe some time + var/can_stab_turfs = FALSE + + /// Effect on hitting something var/hit_effect = ATTACK_EFFECT_SLASH - /// Effect of the cleave attack + /// Effect of the cleave attack var/cleave_effect = /obj/effect/temp_visual/dir_setting/firing_effect/mecha_swipe /obj/item/mecha_parts/mecha_equipment/melee_weapon/can_attach(obj/mecha/M) @@ -73,7 +75,10 @@ if(target == targloc && !(chassis.occupant.a_intent == INTENT_HELP) && cleave) //If we are targetting a location, not an object or mob, and we're not in a passive stance cleave_attack() else if(precise_attacks && (get_dist(src,target) <= (1 + extended_range)) && can_stab_at(chassis, target) && !istype(target, /obj/item) && !istype(target, /obj/effect)) //If we are targetting something stabbable and they're within reach - precise_attack(target) //We stab it if we can + if(targloc == curloc && !can_stab_turfs) + return 0 //Don't stab turf if we can't + else + precise_attack(target) else if(cleave) cleave_attack() //Or swing wildly else //Failure to sword