Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions code/__DEFINES/__starfly.dm
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

#define STARFLY13_MODULE_ADMIN_VERB_FREEZE_ENABLED
#define STARFLY13_MODULE_CRYOSLEEP_SYMPTOMS_FLAG_ENABLED
#define STARFLY13_MODULE_GUN_LORE_ENABLED
#define STARFLY13_MODULE_OXYGEN_DAMAGE_MOD_ENABLED
#define STARFLY13_MODULE_PATCH_UPSTREAM_ENABLED
#define STARFLY13_MODULE_RKSH_UNATHI_ENABLED
Expand Down
12 changes: 12 additions & 0 deletions code/__DEFINES/guns.dm
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,19 @@
#define MANUFACTURER_SHARPLITE "the Sharplite Defense logo"
#define MANUFACTURER_SHARPLITE_NEW "the Nanotrasen-Sharplite logo"
#define MANUFACTURER_HUNTERSPRIDE "the Hunter's Pride Arms and Ammunition logo"
//---------------------------------------------------------------------------------------------------------------------
// STARFLY EDIT - CHANGE BEGIN
#ifndef STARFLY13_MODULE_GUN_LORE_ENABLED
//---------------------------------------------------------------------------------------------------------------------
#define MANUFACTURER_SOLARARMORIES "the Solarbundswaffenkammer emblem"
//---------------------------------------------------------------------------------------------------------------------
#else
//---------------------------------------------------------------------------------------------------------------------
#define MANUFACTURER_SOLARARMORIES "the Solarian emblem"
//---------------------------------------------------------------------------------------------------------------------
#endif // #ifndef STARFLY13_MODULE_GUN_LORE_ENABLED
// STARFLY EDIT - CHANGE END
//---------------------------------------------------------------------------------------------------------------------
#define MANUFACTURER_SCARBOROUGH "the Scarborough Arms logo"
#define MANUFACTURER_EOEHOMA "the Eoehoma Firearms emblem"
#define MANUFACTURER_NANOTRASEN_OLD "an outdated Nanotrasen logo"
Expand Down
7 changes: 6 additions & 1 deletion modular_starfly/NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Some notes about the current state of modularization of previous STARFLY-13 cont


## More Needs
More things that we'll need to create to suport Modular Starfly
More things that we'll need to create to support Modular Starfly

- [ ] Create a CI action to compile shiptest.dmb with each module enabled/disabled
- [ ] Create a CI action to compare `code/__DEFINES/__STARFLY/__modules.dm` with `tgui/packages/tgui/starfly.ts`
Expand Down Expand Up @@ -56,3 +56,8 @@ Upstream changes we don't intend to modularize

- [-] Create MAGIC module (https://github.com/shiptest-ss13/Shiptest/pull/2877)
- [-] Create MUTATION_TOXIN module (https://github.com/shiptest-ss13/Shiptest/pull/2659)

## Further Ports
Changes to sister servers that we would like to copy/influence our own.

- [-] MODS touch-up! (https://github.com/PentestSS13/Pentest/pull/443)
40 changes: 40 additions & 0 deletions modular_starfly/modules/gun_lore/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Starfly-13 Gun Lore

Module ID: `GUN_LORE`

## Description

Updates weapon availability in the marketplace, introduces new manufacturers, adds weapons, reassigns weapons, big lore changes all around.

## TG Proc/File Changes

- N/A
<!-- If you edited any core procs, you should list them here. You should specify the files and procs you changed.
E.g:
- `code/modules/mob/living.dm`: `proc/overriden_proc`, `var/overriden_var`
-->

## Modular Overrides

- N/A
<!-- If you added a new modular override (file or code-wise) for your module, you should list it here. Code files should specify what procs they changed, in case of multiple modules using the same file.
E.g:
- `modular_starfly/master_files/sound/my_cool_sound.ogg`
- `modular_starfly/master_files/code/my_modular_override.dm`: `proc/overriden_proc`, `var/overriden_var`
-->

## Defines

- N/A
<!-- If you needed to add any defines, mention the files you added those defines in, along with the name of the defines. -->

## Included files that are not contained in this module

- N/A
<!-- Likewise, be it a non-modular file or a modular one that's not contained within the folder belonging to this specific module, it should be mentioned here. Good examples are icons or sounds that are used between multiple modules, or other such edge-cases. -->

## Credits

- Patrick Meade created this module.
- LectroNyx is the original author of the content of this module.
- Most of this content first appeared here: https://github.com/Starfly-13/STARFLY-13/pull/55
31 changes: 31 additions & 0 deletions modular_starfly/modules/gun_lore/_defines.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifdef STARFLY13_MODULE_GUN_LORE_ENABLED
//---------------------------------------------------------------------------------------------------------------------

// _defines.dm
// Copyright 2024 LectroNyx.
// Copyright 2026 Patrick Meade.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//---------------------------------------------------------------------------

// See also: code/__DEFINES/guns.dm

#define MANUFACTURER_ROSEUS "the Roseus Galactic logo"
#define MANUFACTURER_ADHOMAI "a Tajaran emblem"
#define MANUFACTURER_LAKVAR "the letters LKV"
#define MANUFACTURER_DONKCO "the Donk! Co. logo"
#define MANUFACTURER_HEPHAESTUS "the Hephaestus Industries logo"

//---------------------------------------------------------------------------------------------------------------------
#endif // #ifdef STARFLY13_MODULE_GUN_LORE_ENABLED
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
//TODO: make this code more readable. weird var names, convoluted logic, etc

//Boxes of ammo
/obj/item/ammo_box
name = "ammo box (null_reference_exception)"
desc = "A box of ammo."
icon = 'icons/obj/ammunition/ammo.dmi'
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BELT
item_state = "syringe_kit"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
custom_materials = list(/datum/material/iron = 15000)
throwforce = 2
w_class = WEIGHT_CLASS_SMALL
throw_speed = 3
throw_range = 7
///list containing the actual ammo within the magazine
var/list/stored_ammo = list()
///type that the magazine will be searching for, rejects if not a subtype of
var/ammo_type = /obj/item/ammo_casing
///maximum amount of ammo in the magazine
var/max_ammo = 7
///Controls how sprites are updated for the ammo box; see defines in combat.dm: AMMO_BOX_ONE_SPRITE; AMMO_BOX_PER_BULLET; AMMO_BOX_FULL_EMPTY
var/multiple_sprites = AMMO_BOX_ONE_SPRITE
///String, used for checking if ammo of different types but still fits can fit inside it; generally used for magazines
var/caliber
///Allows multiple bullets to be loaded in from one click of another box/magazine
var/multiload = FALSE
///Whether or not an ammo box skips the do_after process (e.g. speedloaders)
var/instant_load = FALSE
///Whether the magazine should start with nothing in it
var/start_empty = FALSE
///cost of all the bullets in the magazine/box
var/list/bullet_cost
///cost of the materials in the magazine/box itself
var/list/base_cost

/obj/item/ammo_box/Initialize(mapload, spawn_empty)
. = ..()
if(spawn_empty)
start_empty = TRUE
if(!base_icon_state)
base_icon_state = icon_state

if(!bullet_cost)
for (var/material in custom_materials)
var/material_amount = custom_materials[material]
LAZYSET(base_cost, material, (material_amount * 0.10))

material_amount *= 0.90 // 10% for the container
material_amount /= max_ammo
LAZYSET(bullet_cost, material, material_amount).

if(!start_empty)
top_off(starting = TRUE)

update_appearance()

/*
* top_off is used to refill the magazine to max, in case you want to increase the size of a magazine with VV then refill it at once
* Arguments:
* load_type - if you want to specify a specific ammo casing type to load, enter the path here, otherwise it'll use the basic [/obj/item/ammo_box/var/ammo_type]. Must be a compatible round
* starting - Relevant for revolver cylinders, if FALSE then we mind the nulls that represent the empty cylinders (since those nulls don't exist yet if we haven't initialized when this is TRUE)
* amount - the amount of bullets we're putting in the mag. Otherwise fill it to full if unspecified
*/
/obj/item/ammo_box/proc/top_off(load_type, starting=FALSE, amount)
if(!load_type) //this check comes first so not defining an argument means we just go with default ammo
load_type = ammo_type

var/obj/item/ammo_casing/round_check = load_type
if(!starting && (caliber && initial(round_check.caliber) != caliber) || (!caliber && load_type != ammo_type))
stack_trace("Tried loading unsupported ammocasing type [load_type] into ammo box [type].")
return

var/num_to_load = max_ammo
if(amount)
num_to_load = amount

for(var/i = max(1, stored_ammo.len), i <= num_to_load, i++)
stored_ammo += new round_check(src)

/obj/item/ammo_box/Destroy()
stored_ammo.Cut()
return ..()

///gets a round from the magazine, if keep is TRUE the round will stay in the gun
/obj/item/ammo_box/proc/get_round(keep = FALSE)
if(!stored_ammo.len)
return null
else
var/b = stored_ammo[stored_ammo.len]
stored_ammo -= b
if (keep)
stored_ammo.Insert(1,b)
return b

///puts a round into the magazine
/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = FALSE)
// Boxes don't have a caliber type, magazines do. Not sure if it's intended or not, but if we fail to find a caliber, then we fall back to ammo_type.
if(!R || (caliber && R.caliber != caliber) || (!caliber && R.type != ammo_type))
return FALSE

if(stored_ammo.len < max_ammo)
stored_ammo += R
R.forceMove(src)
return TRUE

//for accessibles magazines (e.g internal ones) when full, start replacing spent ammo
else if(replace_spent)
for(var/obj/item/ammo_casing/AC in stored_ammo)
if(!AC.BB)//found a spent ammo
stored_ammo -= AC
AC.forceMove(get_turf(src.loc))

stored_ammo += R
R.forceMove(src)
return TRUE
return FALSE

///Whether or not the box can be loaded, used in overrides
/obj/item/ammo_box/proc/can_load(mob/user)
return TRUE

/obj/item/ammo_box/attackby(obj/item/attacking_obj, mob/user, params, silent = FALSE, replace_spent = FALSE)
var/num_loaded = 0

if(!can_load(user))
return

if(istype(attacking_obj, /obj/item/ammo_box))
var/obj/item/ammo_box/attacking_box = attacking_obj
for(var/obj/item/ammo_casing/casing_to_insert in attacking_box.stored_ammo)
if(!((instant_load && attacking_box.instant_load) || (stored_ammo.len >= max_ammo) || istype(attacking_obj, /obj/item/ammo_box/magazine/ammo_stack) && do_after(user, 0.5 SECONDS, attacking_box, timed_action_flags = IGNORE_USER_LOC_CHANGE)))
break
var/did_load = give_round(casing_to_insert, replace_spent)
if(!did_load)
break
attacking_box.stored_ammo -= casing_to_insert
if(!silent)
playsound(get_turf(attacking_box), 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE) //src is nullspaced, which means internal magazines won't properly play sound, thus we use attacking_box
num_loaded++
attacking_box.update_ammo_count()
update_ammo_count()

if(istype(attacking_obj, /obj/item/ammo_casing))
var/obj/item/ammo_casing/casing_to_insert = attacking_obj
if(give_round(casing_to_insert, replace_spent))
user.transferItemToLoc(casing_to_insert, src, TRUE)
num_loaded++
casing_to_insert.update_appearance()
update_ammo_count()

if(num_loaded)
if(!silent)
to_chat(user, span_notice("You load [num_loaded] cartridge\s into \the [src]!"))
playsound(src, 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE)
return num_loaded

/obj/item/ammo_box/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
var/num_loaded = 0
var/obj/item/storage/belt/bandolier/to_load
if(istype(target,/obj/item/storage/belt/bandolier))
to_load = target
var/datum/component/storage/storage_to_load = to_load.GetComponent(/datum/component/storage)
for(var/obj/item/ammo_casing/casing_to_insert in stored_ammo)
if(!((to_load.contents.len >= storage_to_load.get_max_volume()) || do_after(user, 0.5 SECONDS, src)))
break
if(!storage_to_load.can_be_inserted(casing_to_insert,TRUE,user))
break
storage_to_load.handle_item_insertion(casing_to_insert,TRUE,user)
stored_ammo -= casing_to_insert
playsound(get_turf(src), 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE)
num_loaded++
update_ammo_count()
if(num_loaded)
to_chat(user, span_notice("You load [num_loaded] cartridge\s into \the [to_load]!"))
return

/obj/item/ammo_box/attack_self(mob/user)
var/obj/item/ammo_casing/A = get_round()
if(!A)
return

A.forceMove(drop_location())
var/mob/living/carbon/human/H = user
if(!(user.is_holding(src) || H.l_store == src || H.r_store == src) || !user.put_in_hands(A)) //incase they're using TK
A.bounce_away(FALSE, NONE)
playsound(src, 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE)
to_chat(user, span_notice("You remove a round from [src]!"))
update_ammo_count()

/// Updates the materials and appearance of this ammo box
/obj/item/ammo_box/proc/update_ammo_count()
update_custom_materials()
update_appearance()

/obj/item/ammo_box/update_desc(updates)
. = ..()
var/shells_left = LAZYLEN(stored_ammo)
desc = "[initial(desc)] There [(shells_left == 1) ? "is" : "are"] [shells_left] shell\s left!"

/obj/item/ammo_box/update_icon_state()
var/shells_left = LAZYLEN(stored_ammo)
switch(multiple_sprites)
if(AMMO_BOX_PER_BULLET)
icon_state = "[base_icon_state]-[shells_left]"
if(AMMO_BOX_FULL_EMPTY)
icon_state = "[base_icon_state]-[shells_left ? "1" : "0"]"
return ..()

/// Updates the amount of material in this ammo box according to how many bullets are left in it.
/obj/item/ammo_box/proc/update_custom_materials()
var/temp_materials = custom_materials.Copy()
for(var/material in bullet_cost)
temp_materials[material] = (bullet_cost[material] * stored_ammo.len) + base_cost[material]
set_custom_materials(temp_materials)

/obj/item/ammo_box/AltClick(mob/user)
if(ishuman(user))
var/mob/living/carbon/human/H = user
if((user.is_holding(src) ||H.l_store == src || H.r_store == src) && !(caliber || istype(src, /obj/item/ammo_box/magazine) || instant_load)) //caliber because boxes have none, instant load because speedloaders use the base ammo box type with instant load on, and magazine for the obvious.
attack_self(user)
return
..()

/obj/item/ammo_box/examine(mob/user)
. = ..()
if(!(caliber || istype(src, /obj/item/ammo_box/magazine) || instant_load))
. += "Alt-click on [src] while it in a pocket or your off-hand to take out a round while it is there."

/obj/item/ammo_box/fire_act(exposed_temperature, exposed_volume)
. = ..()
for(var/obj/item/ammo_casing/bullet2pop in stored_ammo)
bullet2pop.fire_act()

/obj/item/ammo_box/magazine
w_class = WEIGHT_CLASS_SMALL //Default magazine weight, only differs for tiny mags and drums

///Count of number of bullets in the magazine
/obj/item/ammo_box/magazine/proc/ammo_count(countempties = TRUE)
var/boolets = 0
for(var/obj/item/ammo_casing/bullet in stored_ammo)
if(bullet && (bullet.BB || countempties))
boolets++
return boolets

///list of every bullet in the magazine
/obj/item/ammo_box/magazine/proc/ammo_list(drop_list = FALSE)
var/list/L = stored_ammo.Copy()
if(drop_list)
stored_ammo.Cut()
update_ammo_count()
return L

///drops the entire contents of the magazine on the floor
/obj/item/ammo_box/magazine/proc/empty_magazine()
var/turf_mag = get_turf(src)
for(var/obj/item/ammo in stored_ammo)
ammo.forceMove(turf_mag)
stored_ammo -= ammo
update_ammo_count()

/obj/item/ammo_box/magazine/handle_atom_del(atom/A)
stored_ammo -= A
update_ammo_count()

Loading