Skip to content

Commit

Permalink
Sets up structs (pseudo-structs) as a replacement for datums for simp…
Browse files Browse the repository at this point in the history
…le data storage

- Replaces launch metadata datum with a struct

- Some minor variable renaming, not super comprehensive tho
  • Loading branch information
boskoramen committed Sep 1, 2024
1 parent f9cd32b commit bb7334e
Show file tree
Hide file tree
Showing 19 changed files with 294 additions and 156 deletions.
31 changes: 31 additions & 0 deletions code/__DEFINES/_struct.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#define DEFSTRUCT(type) /datum/struct/##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++; \
}

#define ENDDEFSTRUCT(type) \
/datum/struct/##type/proc/generate() { \
var/list/struct = new /list(src.len); \
struct[1] = #type; \
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 PROP(type, prop) STRUCTS.##type.##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] == #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, type) \
do { \
if(struct) { \
for(var/i in 1 to length(struct)) { \
if(STRUCTS.##type.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, type) \
if(struct) { \
for(var/i in 1 to length(struct)) { \
if(!isnull(struct[i]) && STRUCTS.##type.can_qdel[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!")
19 changes: 19 additions & 0 deletions code/datums/structs/_struct.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/datum/struct
/// 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/static/list/can_qdel

/datum/struct/New()
// 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
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, launch_metadata)
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 = launch_metadata[PROP(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 = launch_metadata[PROP(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 = AM.launch_metadata[PROP(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)
launch_metadata[PROP(launch_metadata, target)] = target_turf
launch_metadata[PROP(launch_metadata, range)] = get_dist(AM.loc, target_turf)
launch_metadata[PROP(launch_metadata, speed)] = SPEED_FAST
launch_metadata[PROP(launch_metadata, thrower)] = unlucky_mob
launch_metadata[PROP(launch_metadata, spin)] = TRUE
launch_metadata[PROP(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
84 changes: 43 additions & 41 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 = launch_metadata[PROP(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)
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

0 comments on commit bb7334e

Please sign in to comment.