Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sets up structs (pseudo-structs) as a replacement for datums for si… #7077

Closed
wants to merge 5 commits into from
Closed
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
32 changes: 32 additions & 0 deletions code/__DEFINES/_struct.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#define DEFSTRUCT(type) /datum/struct/##type; /datum/struct/##type/name = #type;

#define DEFSTRUCTPROP(type, prop, _can_qdel) \
/datum/struct/##type/var/##prop; \
/datum/struct/##type/add_props() { \
..(); \
can_qdel += _can_qdel; \
##prop = STRUCTS.current_struct_idx++; \
props += #prop; \
}

#define ENDDEFSTRUCT(type) \
/datum/struct/##type/proc/generate() { \
var/list/struct = new /list(src.len); \
struct[1] = src; \
return struct; \
} \
/datum/controller/structs/var/datum/struct/##type/##type; \
/datum/controller/structs/proc/generate_struct_##type() { \
##type = new(); \
}

#define STRUCT(type) STRUCTS.##type.generate();
#define GETPROP(struct, prop) struct[struct[1].##prop]

// Fun pseudo function: List.[1] works
// However SpacemanDMM throws a fit about the syntax
// #define PROP(type, prop) [STRUCTS.##type.##prop]

#define ISSTRUCT(to_test, type) (length(to_test) ? to_test[1] == STRUCTS.##type : FALSE)

#define STRUCT_PROP_STARTING_INDEX 2
34 changes: 33 additions & 1 deletion code/__DEFINES/qdel.dm
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,40 @@
#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), (time) > GC_FILTER_QUEUE ? WEAKREF(item) : item), time, TIMER_STOPPABLE)
#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME)
#define QDEL_NULL(item) qdel(item); item = null
#define QDEL_NULL_LIST(x) if(x) { for(var/y in x) { qdel(y) }}; if(x) {x.Cut(); x = null } // Second x check to handle items that LAZYREMOVE on qdel.

// Second x check to handle items that LAZYREMOVE on qdel.
#define QDEL_NULL_LIST(x) \
do { \
if(x) { for(var/y in x) { qdel(y) }}; \
if(x) {x.Cut(); x = null } \
} while(FALSE);

// Second struct check to handle items that LAZYREMOVE on qdel.
#define QDEL_NULL_STRUCT(struct) \
do { \
if(struct) { \
for(var/i in STRUCT_PROP_STARTING_INDEX to length(struct)) { \
if(struct[1].can_qdel[i] && !isnull(struct[i])) { \
qdel(struct[i]); \
} \
} \
}; \
if(struct) { \
struct.Cut(); \
struct = null; \
}; \
} while(FALSE);

#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); }
#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(______qdel_list_wrapper), L), time, TIMER_STOPPABLE)
#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); }
#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); }
#define QDEL_STRUCT(struct) \
if(struct) { \
for(var/i in STRUCT_PROP_STARTING_INDEX to length(struct)) { \
if(struct[1].can_qdel[i] && !isnull(struct[i])) { \
qdel(struct[i]); \
} \
}; \
struct.Cut(); \
}
1 change: 1 addition & 0 deletions code/_globalvars/struct.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GLOBAL_REAL(STRUCTS, /datum/controller/structs)
2 changes: 2 additions & 0 deletions code/controllers/mc/master.dm
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new

if(!GLOB)
new /datum/controller/global_vars
if(!STRUCTS)
new /datum/controller/structs

/datum/controller/master/Destroy()
..()
Expand Down
43 changes: 43 additions & 0 deletions code/controllers/struct.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/datum/controller/structs
/**
* Value for calculating the index of the struct currently being
* generated
* Should always be above 1, as index 1 is reserved for the struct name
*/
var/static/current_struct_idx = STRUCT_PROP_STARTING_INDEX

var/list/structs_datum_in_built_vars

/datum/controller/structs/New()
if(STRUCTS)
CRASH("Multiple instances of structs controller created")
STRUCTS = src

var/datum/controller/exclude_these = new
// I know this is dumb but the nested vars list hangs a ref to the datum. This fixes that
var/list/controller_vars = exclude_these.vars.Copy()
controller_vars["vars"] = null
structs_datum_in_built_vars = controller_vars + list(NAMEOF(src, current_struct_idx))

QDEL_IN(exclude_these, 0) //signal logging isn't ready

log_world("[length(vars) - length(structs_datum_in_built_vars)] structs")

Initialize()

/datum/controller/structs/Initialize()
var/list/struct_initializers = typesof(/datum/controller/structs/proc)
var/expected_len = length(vars) - length(structs_datum_in_built_vars)
if(length(struct_initializers) != expected_len)
warning("Unable to detect all struct initialization procs! Expected [expected_len] got [length(struct_initializers)]!")
if(length(struct_initializers))
var/list/expected_struct_initializers = vars - structs_datum_in_built_vars
for(var/initializer in struct_initializers)
expected_struct_initializers -= replacetext("[initializer]", "generate_struct_", "")
log_world("Structs with missing initializers: [expected_struct_initializers.Join(", ")]")
for(var/initializer in struct_initializers)
var/start_tick = world.time
call(src, initializer)()
var/end_tick = world.time
if(end_tick - start_tick)
warning("Struct [replacetext("[initializer]", "generate_struct_", "")] slept during initialization!")
25 changes: 25 additions & 0 deletions code/datums/structs/_struct.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/datum/struct
var/name = "UNDEFINED"
var/list/props
/// The length of the lists to be generated
/// which represent a struct
var/len

/// Standard list mapped 1:1 to the list produced when
/// struct is created using STRUCT(...)
var/list/can_qdel

/datum/struct/New()
props = list()
// First element of struct list is a string containing the struct name
can_qdel = list(FALSE)
STRUCTS.current_struct_idx = STRUCT_PROP_STARTING_INDEX
add_props()
// Index will go up by 1 when adding last element
len = STRUCTS.current_struct_idx - 1

/datum/struct/proc/add_props()
return

/datum/struct/proc/operator""()
return "struct [name]([props.Join(", ")])"
12 changes: 12 additions & 0 deletions code/datums/structs/launch_metadata.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
DEFSTRUCT(launch_metadata)
DEFSTRUCTPROP(launch_metadata, pass_flags, FALSE)
DEFSTRUCTPROP(launch_metadata, call_all, FALSE)
DEFSTRUCTPROP(launch_metadata, target, FALSE)
DEFSTRUCTPROP(launch_metadata, range, FALSE)
DEFSTRUCTPROP(launch_metadata, speed, FALSE)
DEFSTRUCTPROP(launch_metadata, thrower, FALSE)
DEFSTRUCTPROP(launch_metadata, spin, FALSE)
DEFSTRUCTPROP(launch_metadata, collision_callbacks, FALSE)
DEFSTRUCTPROP(launch_metadata, end_throw_callbacks, FALSE)
DEFSTRUCTPROP(launch_metadata, dist, FALSE)
ENDDEFSTRUCT(launch_metadata)
2 changes: 1 addition & 1 deletion code/game/atoms_movable.dm
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
qdel(I)
if(pulledby)
pulledby.stop_pulling()
QDEL_NULL(launch_metadata)
QDEL_STRUCT(launch_metadata)

Check warning on line 43 in code/game/atoms_movable.dm

View workflow job for this annotation

GitHub Actions / Run Linters

field access requires static type: "can_qdel"
QDEL_NULL(em_block)
QDEL_NULL(emissive_overlay)

Expand Down
9 changes: 5 additions & 4 deletions code/game/objects/items/explosives/grenades/grenade.dm
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,11 @@
dangerous = FALSE
. = ..()

/obj/item/explosive/grenade/launch_towards(datum/launch_metadata/LM)
if(active && ismob(LM.thrower))
var/mob/M = LM.thrower
M.count_niche_stat(STATISTICS_NICHE_GRENADES)
/obj/item/explosive/grenade/launch_towards(list/launch_metadata)
var/thrower = GETPROP(launch_metadata, thrower)
if(active && ismob(thrower))
var/mob/mob_thrower = thrower
mob_thrower.count_niche_stat(STATISTICS_NICHE_GRENADES)
. = ..()


Expand Down
3 changes: 1 addition & 2 deletions code/game/objects/items/shards.dm
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,7 @@

/obj/item/large_shrapnel/at_rocket_dud/launch_impact(atom/hit_atom)
. = ..()
var/datum/launch_metadata/LM = src.launch_metadata
var/user = LM.thrower
var/user = GETPROP(launch_metadata, thrower)
if(!detonating && prob(impact_sensitivity))
cause = "manually triggered"
visible_message(SPAN_DANGER("You hear the click of a mechanism triggering inside \the [src]. Uh oh."))
Expand Down
3 changes: 2 additions & 1 deletion code/game/objects/structures/window.dm
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@
anchored = FALSE
update_nearby_icons()
step(src, get_dir(AM, src))
healthcheck(user = AM.launch_metadata.thrower)
var/source = GETPROP(AM.launch_metadata, thrower)
healthcheck(user = source)

/obj/structure/window/attack_hand(mob/user as mob)
if(user.a_intent == INTENT_HARM && ishuman(user))
Expand Down
16 changes: 8 additions & 8 deletions code/game/turfs/open.dm
Original file line number Diff line number Diff line change
Expand Up @@ -779,15 +779,15 @@
return
var/mob/unlucky_mob = AM
var/turf/target_turf = get_random_turf_in_range(AM.loc, 3, 0)
var/datum/launch_metadata/LM = new()
LM.target = target_turf
LM.range = get_dist(AM.loc, target_turf)
LM.speed = SPEED_FAST
LM.thrower = unlucky_mob
LM.spin = TRUE
LM.pass_flags = NO_FLAGS
var/list/launch_metadata = STRUCT(launch_metadata)
GETPROP(launch_metadata, target) = target_turf
GETPROP(launch_metadata, range) = get_dist(AM.loc, target_turf)
GETPROP(launch_metadata, speed) = SPEED_FAST
GETPROP(launch_metadata, thrower) = unlucky_mob
GETPROP(launch_metadata, spin) = TRUE
GETPROP(launch_metadata, pass_flags) = NO_FLAGS
to_chat(unlucky_mob, SPAN_WARNING("The ocean currents sweep you off your feet and throw you away!"))
unlucky_mob.launch_towards(LM)
unlucky_mob.launch_towards(launch_metadata)
return

if(world.time % 5)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/cm_preds/smartdisc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
force = 15
throwforce = 25

/obj/item/explosive/grenade/spawnergrenade/smartdisc/launch_towards(datum/launch_metadata/LM)
/obj/item/explosive/grenade/spawnergrenade/smartdisc/launch_towards(list/launch_metadata)
..()
var/mob/user = usr
if(!active && isyautja(user) && (icon_state == initial(icon_state)))
Expand Down
86 changes: 44 additions & 42 deletions code/modules/mob/living/carbon/human/human_defense.dm
Original file line number Diff line number Diff line change
Expand Up @@ -265,96 +265,98 @@ Contains most of the procs that are called when a mob is attacked by something
return TRUE

//this proc handles being hit by a thrown atom
/mob/living/carbon/human/hitby(atom/movable/AM)
if (!isobj(AM))
/mob/living/carbon/human/hitby(atom/movable/thrown_atom)
if (!isobj(thrown_atom))
return

var/obj/O = AM
var/datum/launch_metadata/LM = O.launch_metadata
var/obj/thrown_object = thrown_atom
var/list/launch_metadata = thrown_object.launch_metadata

//empty active hand and we're in throw mode
var/can_catch = (!(O.flags_atom & ITEM_UNCATCHABLE) || isyautja(src))
var/can_catch = (!(thrown_object.flags_atom & ITEM_UNCATCHABLE) || isyautja(src))
if (throw_mode && can_catch && !get_active_hand() && cur_speed <= SPEED_VERY_FAST && \
!is_mob_incapacitated() && isturf(O.loc) && put_in_active_hand(O)
!is_mob_incapacitated() && isturf(thrown_object.loc) && put_in_active_hand(thrown_object)
)
visible_message(SPAN_WARNING("[src] catches [O]!"), null, null, 5)
visible_message(SPAN_WARNING("[src] catches [thrown_object]!"), null, null, 5)
toggle_throw_mode(THROW_MODE_OFF)
return

var/dtype = BRUTE
if (istype(O, /obj/item/weapon))
var/obj/item/weapon/W = O
dtype = W.damtype
var/impact_damage = (1 + O.throwforce*THROWFORCE_COEFF)*O.throwforce*THROW_SPEED_IMPACT_COEFF*O.cur_speed
if (istype(thrown_object, /obj/item/weapon))
var/obj/item/weapon/thrown_weapon = thrown_object
dtype = thrown_weapon.damtype
var/impact_damage = (1 + thrown_object.throwforce*THROWFORCE_COEFF)*thrown_object.throwforce*THROW_SPEED_IMPACT_COEFF*thrown_object.cur_speed

var/zone
if (istype(LM.thrower, /mob/living))
var/mob/living/L = LM.thrower
zone = check_zone(L.zone_selected)
var/thrower = GETPROP(launch_metadata, thrower)
if (istype(thrower, /mob/living))
var/mob/living/living_thrower = thrower
zone = check_zone(living_thrower.zone_selected)
else
zone = rand_zone("chest", 75) //Hits a random part of the body, geared towards the chest

if (!zone)
visible_message(SPAN_NOTICE("\The [O] misses [src] narrowly!"), null, null, 5)
visible_message(SPAN_NOTICE("\The [thrown_object] misses [src] narrowly!"), null, null, 5)
return
O.throwing = FALSE //it hit, so stop moving
thrown_object.throwing = FALSE //it hit, so stop moving

if ((LM.thrower != src) && check_shields(impact_damage, "[O]"))
if ((thrower != src) && check_shields(impact_damage, "[thrown_object]"))
return

var/obj/limb/affecting = get_limb(zone)
var/hit_area = affecting.display_name

src.visible_message(SPAN_DANGER("[src] has been hit in the [hit_area] by [O]."), null, null, 5)
src.visible_message(SPAN_DANGER("[src] has been hit in the [hit_area] by [thrown_object]."), null, null, 5)

var/armor = getarmor(affecting, ARMOR_MELEE)

var/weapon_sharp = is_sharp(O)
var/weapon_edge = has_edge(O)
var/weapon_sharp = is_sharp(thrown_object)
var/weapon_edge = has_edge(thrown_object)

var/damage = armor_damage_reduction(GLOB.marine_melee, impact_damage, armor, (weapon_sharp?30:0) + (weapon_edge?10:0))
apply_damage(damage, dtype, affecting, sharp=weapon_sharp, edge=weapon_edge, used_weapon=O)
apply_damage(damage, dtype, affecting, sharp=weapon_sharp, edge=weapon_edge, used_weapon=thrown_object)

var/last_damage_source = null
var/last_damage_mob = null
if (damage > 5)
last_damage_source = initial(AM.name)
last_damage_source = initial(thrown_atom.name)
animation_flash_color(src)
var/obj/item/I = O
if(istype(I) && I.sharp) //Hilarious is_sharp only returns true if it's sharp AND edged, while a bunch of things don't have edge to limit embeds.
var/obj/item/thrown_item = thrown_object
if(istype(thrown_item) && thrown_item.sharp) //Hilarious is_sharp only returns true if it's sharp AND edged, while a bunch of things don't have edge to limit embeds.
playsound(loc, 'sound/effects/spike_hit.ogg', 20, TRUE, 5, falloff = 2)
else
playsound(loc, 'sound/effects/thud.ogg', 25, TRUE, 5, falloff = 2)

if (ismob(LM.thrower))
var/mob/M = LM.thrower
var/client/assailant = M.client
if (ismob(thrower))
var/mob/mob_thrower = thrower
var/client/assailant = mob_thrower.client
if (damage > 5)
last_damage_mob = M
M.track_hit(initial(O.name))
if (M.faction == faction)
M.track_friendly_fire(initial(O.name))
last_damage_mob = mob_thrower
mob_thrower.track_hit(initial(thrown_object.name))
if (mob_thrower.faction == faction)
mob_thrower.track_friendly_fire(initial(thrown_object.name))
if (assailant)
src.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been hit with \a [O], thrown by [key_name(M)]</font>")
M.attack_log += text("\[[time_stamp()]\] <font color='red'>Hit [key_name(src)] with a thrown [O]</font>")
src.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been hit with \a [thrown_object], thrown by [key_name(mob_thrower)]</font>")
mob_thrower.attack_log += text("\[[time_stamp()]\] <font color='red'>Hit [key_name(src)] with a thrown [thrown_object]</font>")
if(!istype(src,/mob/living/simple_animal/mouse))
msg_admin_attack("[key_name(src)] was hit by \a [O], thrown by [key_name(M)] in [get_area(src)] ([src.loc.x],[src.loc.y],[src.loc.z]).", src.loc.x, src.loc.y, src.loc.z)
msg_admin_attack("[key_name(src)] was hit by \a [thrown_object], thrown by [key_name(mob_thrower)] in [get_area(src)] ([src.loc.x],[src.loc.y],[src.loc.z]).", src.loc.x, src.loc.y, src.loc.z)

if(last_damage_source)
if (last_damage_source)
last_damage_data = create_cause_data(last_damage_source, last_damage_mob)

//thrown weapon embedded object code.
if (dtype == BRUTE && istype(O,/obj/item))
var/obj/item/I = O
var/sharp = is_sharp(I)
if (dtype == BRUTE && istype(thrown_object, /obj/item))
var/obj/item/thrown_item = thrown_object
var/sharp = is_sharp(thrown_item)
var/weight_class = thrown_item.w_class
//blunt objects should really not be embedding in things unless a huge amount of force is involved
var/embed_chance = sharp? damage/I.w_class : damage/(I.w_class*3)
var/embed_threshold = sharp? 5*I.w_class : 15*I.w_class
var/embed_chance = sharp ? damage/weight_class : damage/(weight_class*3)
var/embed_threshold = sharp ? 5*weight_class : 15*weight_class

//Sharp objects will always embed if they do enough damage.
//Thrown sharp objects have some momentum already and have a small chance to embed even if the damage is below the threshold
if (!isyautja(src) && ((sharp && prob(damage/(10*I.w_class)*100)) || (damage > embed_threshold && prob(embed_chance))))
affecting.embed(I)
if (!isyautja(src) && ((sharp && prob(damage/(10*weight_class)*100)) || (damage > embed_threshold && prob(embed_chance))))
affecting.embed(thrown_item)

/mob/living/carbon/human/proc/get_id_faction_group()
var/obj/item/card/id/C = wear_id
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@
return TRUE
return FALSE

/obj/item/clothing/mask/facehugger/launch_towards(datum/launch_metadata/LM)
/obj/item/clothing/mask/facehugger/launch_towards(list/launch_metadata)
if(stat == CONSCIOUS)
icon_state = "[initial(icon_state)]_thrown"
..()
Expand Down
Loading
Loading