Skip to content

Commit

Permalink
Easy Energy Cells -- Integrated Retainment Latches (#3853)
Browse files Browse the repository at this point in the history
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

## About The Pull Request

All energy weapons now come with a retainment clip for their energy
cells, if their cells are designed to be removable. After a cell is
unlatched, a user will be able to remove the cell like as they would a
magazine from a ballistic weapon. However, one must remember that these
retainment latches were designed into these weapons for a reason. Firing
one with a latch hanging loose will likely cause the cell to fall out
and/or disconnect due to jostling.

In order to latch or unlatch a retainment clip, one must alt-click on
their weapon.

If one is using a dual-mode weapon, such as an E-40, they will need to
switch to laser mode in order to latch and unlatch the retainment clip,
and remove the cell.

If one has attachments on their weapon, you'll need to remove the cell
on their weapon before attempting to remove an attachment. For the E-40,
one simply needs to swap to the ballistic mode in order to do that.

A line indicating the latch status was added to the examine text of
energy weapons.

![image](https://github.com/user-attachments/assets/5bd8d99b-c066-4664-a6e0-6584fe11667f)

This PR also slightly modifies the examine text of the E-40 to include
cell charge characteristics.

![image](https://github.com/user-attachments/assets/5bfadcd4-1ca7-4e10-9e56-c37f382e549e)

Small sample of it in operation:


https://github.com/user-attachments/assets/5ac44c60-fed4-4448-ac15-4f65cf3202b9

Full demo of it in action:
https://youtu.be/Be1c_0AFAds

A clip of the state of the latch sprites at this time:


https://github.com/user-attachments/assets/66d036c1-27df-486f-9b34-c82c22adbd06


## Why It's Good For The Game

Having cell removal be tied to alt-click felt smoother to me, compared
to the current way that cells are handled, with removal requiring a
screwdriver. The time it takes to remove and replace a cell is
comparable as before the change, but it no longer requires a screwdriver
to do.

## Changelog

:cl:
balance: replaced screwdriver cell removal with a cell-retainment clip
mechanism
/:cl:

<!-- Both :cl:'s are required for the changelog to work! You can put
your name to the right of the first :cl: if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->
  • Loading branch information
zimon9 authored Dec 3, 2024
1 parent 392a1b7 commit c0b75db
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 18 deletions.
65 changes: 63 additions & 2 deletions code/modules/projectiles/guns/ballistic/assault.dm
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@
/obj/item/gun/ballistic/automatic/assault/e40/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread)
var/current_firemode = gun_firemodes[firemode_index]
if(current_firemode != FIREMODE_OTHER)
if(!secondary.latch_closed && prob(65))
to_chat(user, span_warning("[src]'s cell falls out!"))
secondary.eject_cell()
return ..()
return secondary.process_fire(target, user, message, params, zone_override, bonus_spread)

Expand All @@ -198,10 +201,42 @@
/obj/item/gun/ballistic/automatic/assault/e40/attackby(obj/item/attack_obj, mob/user, params)
if(istype(attack_obj, /obj/item/stock_parts/cell/gun))
return secondary.attackby(attack_obj, user, params)
if(istype(attack_obj, /obj/item/screwdriver))
return secondary.screwdriver_act(user, attack_obj,)
return ..()

/obj/item/gun/ballistic/automatic/assault/e40/attack_hand(mob/user)
var/current_firemode = gun_firemodes[firemode_index]
if(current_firemode == FIREMODE_OTHER && loc == user && user.is_holding(src) && secondary.cell && !secondary.latch_closed)
secondary.eject_cell(user)
return
if(current_firemode == FIREMODE_OTHER && loc == user && user.is_holding(src) && secondary.cell && secondary.latch_closed)
to_chat(user, span_warning("The cell retainment clip is latched!"))
return
return ..()

/obj/item/gun/ballistic/automatic/assault/e40/AltClick(mob/living/user)
var/current_firemode = gun_firemodes[firemode_index]
if(current_firemode == FIREMODE_OTHER)
if(secondary.latch_closed)
to_chat(user, span_notice("You start to unlatch the [src]'s power cell retainment clip..."))
if(do_after(user, secondary.latch_toggle_delay, src, IGNORE_USER_LOC_CHANGE))
to_chat(user, span_notice("You unlatch [src]'s power cell retainment clip " + "<span class='red'>OPEN</span>" + "."))
playsound(src, 'sound/items/taperecorder/taperecorder_play.ogg', 50, FALSE)
secondary.tac_reloads = TRUE
secondary.latch_closed = FALSE
update_appearance()
return
else
to_chat(user, span_warning("You start to latch the [src]'s power cell retainment clip..."))
if (do_after(user, secondary.latch_toggle_delay, src, IGNORE_USER_LOC_CHANGE))
to_chat(user, span_notice("You latch [src]'s power cell retainment clip " + "<span class='green'>CLOSED</span>" + "."))
playsound(src, 'sound/items/taperecorder/taperecorder_close.ogg', 50, FALSE)
secondary.tac_reloads = FALSE
secondary.latch_closed = TRUE
update_appearance()
return
else
return ..()

/obj/item/gun/ballistic/automatic/assault/e40/on_wield(obj/item/source, mob/user)
wielded = TRUE
secondary.wielded = TRUE
Expand Down Expand Up @@ -241,6 +276,20 @@
. += "[icon_state]_charge[ratio]"
if(secondary.cell)
. += "[icon_state]_cell"
if(ismob(loc))
var/mutable_appearance/latch_overlay
latch_overlay = mutable_appearance('icons/obj/guns/cell_latch.dmi')
if(secondary.latch_closed)
if(secondary.cell)
latch_overlay.icon_state = "latch-on-full"
else
latch_overlay.icon_state = "latch-on-empty"
else
if(secondary.cell)
latch_overlay.icon_state = "latch-off-full"
else
latch_overlay.icon_state = "latch-off-empty"
. += latch_overlay


/obj/item/gun/ballistic/automatic/assault/e40/toggle_safety(mob/user, silent=FALSE)
Expand All @@ -257,6 +306,17 @@
SEND_SIGNAL(src, COMSIG_GUN_SET_AUTOFIRE_SPEED, fire_delay)
SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD)

/obj/item/gun/ballistic/automatic/assault/e40/examine(mob/user)
. = ..()
if(!secondary.internal_magazine)
. += "The cell retainment latch is [secondary.latch_closed ? "<span class='green'>CLOSED</span>" : "<span class='red'>OPEN</span>"]. Alt-Click to toggle the latch."
var/obj/item/ammo_casing/energy/shot = secondary.ammo_type[select]
if(secondary.cell)
. += "\The [name]'s cell has [secondary.cell.percent()]% charge remaining."
. += "\The [name] has [round(secondary.cell.charge/shot.e_cost)] shots remaining on <b>[shot.select_name]</b> mode."
else
. += span_notice("\The [name] doesn't seem to have a cell!")

//laser

/obj/item/gun/energy/laser/e40_laser_secondary
Expand All @@ -268,5 +328,6 @@
fire_delay = 0.2 SECONDS
gun_firemodes = list(FIREMODE_FULLAUTO)
default_firemode = FIREMODE_FULLAUTO
latch_toggle_delay = 1.2 SECONDS

spread_unwielded = 20
80 changes: 64 additions & 16 deletions code/modules/projectiles/guns/energy.dm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
tac_reloads = FALSE
tactical_reload_delay = 1.2 SECONDS

var/latch_closed = TRUE
var/latch_toggle_delay = 1.0 SECONDS

valid_attachments = list(
/obj/item/attachment/laser_sight,
/obj/item/attachment/rail_light,
Expand Down Expand Up @@ -128,22 +131,26 @@
if (!internal_magazine && (A.type in (allowed_ammo_types - blacklisted_ammo_types)))
var/obj/item/stock_parts/cell/gun/C = A
if (!cell)
insert_cell(user, C)
return insert_cell(user, C)
else
if (tac_reloads)
eject_cell(user, C)

return ..()

/obj/item/gun/energy/proc/insert_cell(mob/user, obj/item/stock_parts/cell/gun/C)
if(user.transferItemToLoc(C, src))
cell = C
to_chat(user, span_notice("You load the [C] into \the [src]."))
playsound(src, load_sound, load_sound_volume, load_sound_vary)
update_appearance()
return TRUE
if(!latch_closed)
if(user.transferItemToLoc(C, src))
cell = C
to_chat(user, span_notice("You load the [C] into \the [src]."))
playsound(src, load_sound, load_sound_volume, load_sound_vary)
update_appearance()
return TRUE
else
to_chat(user, span_warning("You cannot seem to get \the [src] out of your hands!"))
return FALSE
else
to_chat(user, span_warning("You cannot seem to get \the [src] out of your hands!"))
to_chat(user, span_warning("The [src]'s cell retainment clip is latched!"))
return FALSE

/obj/item/gun/energy/proc/eject_cell(mob/user, obj/item/stock_parts/cell/gun/tac_load = null)
Expand All @@ -167,13 +174,33 @@
user.put_in_hands(old_cell)
update_appearance()

/obj/item/gun/energy/screwdriver_act(mob/living/user, obj/item/I)
if(cell && !internal_magazine)
to_chat(user, span_notice("You begin unscrewing and pulling out the cell..."))
if(I.use_tool(src, user, unscrewing_time, volume = 100))
to_chat(user, span_notice("You remove the power cell."))
eject_cell(user)
return ..()
//special is_type_in_list method to counteract problem with current method
/obj/item/gun/energy/proc/is_attachment_in_contents_list()
for(var/content_item in contents)
if(istype(content_item, /obj/item/attachment/))
return TRUE
return FALSE

/obj/item/gun/energy/AltClick(mob/living/user)
if(!internal_magazine && latch_closed)
to_chat(user, span_notice("You start to unlatch the [src]'s power cell retainment clip..."))
if(do_after(user, latch_toggle_delay, src, IGNORE_USER_LOC_CHANGE))
to_chat(user, span_notice("You unlatch the [src]'s power cell retainment clip " + "<span class='red'>OPEN</span>" + "."))
playsound(src, 'sound/items/taperecorder/taperecorder_play.ogg', 50, FALSE)
tac_reloads = TRUE
latch_closed = FALSE
update_appearance()
else if(!internal_magazine && !latch_closed)
if(!cell && is_attachment_in_contents_list())
return ..() //should bring up the attachment menu if attachments are added. If none are added, it just does leaves the latch open
to_chat(user, span_warning("You start to latch the [src]'s power cell retainment clip..."))
if (do_after(user, latch_toggle_delay, src, IGNORE_USER_LOC_CHANGE))
to_chat(user, span_notice("You latch the [src]'s power cell retainment clip " + "<span class='green'>CLOSED</span>" + "."))
playsound(src, 'sound/items/taperecorder/taperecorder_close.ogg', 50, FALSE)
tac_reloads = FALSE
latch_closed = TRUE
update_appearance()
return

/obj/item/gun/energy/can_shoot(visuals)
if(safety && !visuals)
Expand Down Expand Up @@ -213,7 +240,12 @@
/obj/item/gun/energy/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
if(!chambered && can_shoot())
process_chamber() // If the gun was drained and then recharged, load a new shot.
return ..()
..() //process the gunshot as normal
if(!latch_closed && prob(65)) //make the cell slide out if it's fired while the retainment clip is unlatched, with a 65% probability
to_chat(user, span_warning("The [src]'s cell falls out!"))
eject_cell()
return


/obj/item/gun/energy/proc/select_fire(mob/living/user)
select++
Expand Down Expand Up @@ -252,6 +284,20 @@
var/overlay_icon_state = "[icon_state]_charge"
var/obj/item/ammo_casing/energy/shot = ammo_type[modifystate ? select : 1]
var/ratio = get_charge_ratio()
if(ismob(loc) && !internal_magazine)
var/mutable_appearance/latch_overlay
latch_overlay = mutable_appearance('icons/obj/guns/cell_latch.dmi')
if(latch_closed)
if(cell)
latch_overlay.icon_state = "latch-on-full"
else
latch_overlay.icon_state = "latch-on-empty"
else
if(cell)
latch_overlay.icon_state = "latch-off-full"
else
latch_overlay.icon_state = "latch-off-empty"
. += latch_overlay
if(cell)
. += "[icon_state]_cell"
if(ratio == 0)
Expand Down Expand Up @@ -322,6 +368,8 @@

/obj/item/gun/energy/examine(mob/user)
. = ..()
if(!internal_magazine)
. += "The cell retainment latch is [latch_closed ? "<span class='green'>CLOSED</span>" : "<span class='red'>OPEN</span>"]. Alt-Click to toggle the latch."
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
if(ammo_type.len > 1)
. += "You can switch firemodes by pressing the <b>unique action</b> key. By default, this is <b>space</b>"
Expand Down
Binary file added icons/obj/guns/cell_latch.dmi
Binary file not shown.

0 comments on commit c0b75db

Please sign in to comment.