diff --git a/code/__DEFINES/_struct.dm b/code/__DEFINES/_struct.dm new file mode 100644 index 000000000000..b547a15859b1 --- /dev/null +++ b/code/__DEFINES/_struct.dm @@ -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 diff --git a/code/__DEFINES/qdel.dm b/code/__DEFINES/qdel.dm index 2093f80be50e..f2fee0154885 100644 --- a/code/__DEFINES/qdel.dm +++ b/code/__DEFINES/qdel.dm @@ -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(); \ +} diff --git a/code/_globalvars/struct.dm b/code/_globalvars/struct.dm new file mode 100644 index 000000000000..b79e7b7ca75f --- /dev/null +++ b/code/_globalvars/struct.dm @@ -0,0 +1 @@ +GLOBAL_REAL(STRUCTS, /datum/controller/structs) diff --git a/code/controllers/mc/master.dm b/code/controllers/mc/master.dm index 740e29949418..322398e91ab1 100644 --- a/code/controllers/mc/master.dm +++ b/code/controllers/mc/master.dm @@ -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() ..() diff --git a/code/controllers/struct.dm b/code/controllers/struct.dm new file mode 100644 index 000000000000..d16d79ed5ec9 --- /dev/null +++ b/code/controllers/struct.dm @@ -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!") diff --git a/code/datums/structs/_struct.dm b/code/datums/structs/_struct.dm new file mode 100644 index 000000000000..ddac97e016e6 --- /dev/null +++ b/code/datums/structs/_struct.dm @@ -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 diff --git a/code/datums/structs/launch_metadata.dm b/code/datums/structs/launch_metadata.dm new file mode 100644 index 000000000000..d7867e414bb9 --- /dev/null +++ b/code/datums/structs/launch_metadata.dm @@ -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) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index be68dddff10f..01a31746ee29 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -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) diff --git a/code/game/objects/items/explosives/grenades/grenade.dm b/code/game/objects/items/explosives/grenades/grenade.dm index b2f95646a966..6859262e498c 100644 --- a/code/game/objects/items/explosives/grenades/grenade.dm +++ b/code/game/objects/items/explosives/grenades/grenade.dm @@ -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) . = ..() diff --git a/code/game/objects/items/shards.dm b/code/game/objects/items/shards.dm index dab573e6f5a5..2e56855f90da 100644 --- a/code/game/objects/items/shards.dm +++ b/code/game/objects/items/shards.dm @@ -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.")) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 14e15de24691..4e2a65a9072a 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -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)) diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm index 680348e4d995..2b3efa38838a 100644 --- a/code/game/turfs/open.dm +++ b/code/game/turfs/open.dm @@ -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) diff --git a/code/modules/cm_preds/smartdisc.dm b/code/modules/cm_preds/smartdisc.dm index f0262f5f140a..de6a4b706f46 100644 --- a/code/modules/cm_preds/smartdisc.dm +++ b/code/modules/cm_preds/smartdisc.dm @@ -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))) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 09ce5bb9c149..0c8ea94c1549 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -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()]\] Has been hit with \a [O], thrown by [key_name(M)]") - M.attack_log += text("\[[time_stamp()]\] Hit [key_name(src)] with a thrown [O]") + src.attack_log += text("\[[time_stamp()]\] Has been hit with \a [thrown_object], thrown by [key_name(mob_thrower)]") + mob_thrower.attack_log += text("\[[time_stamp()]\] Hit [key_name(src)] with a thrown [thrown_object]") 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 diff --git a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm index 9a87f10d74a3..c2b8e4912b28 100644 --- a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm +++ b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm @@ -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" ..() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 27697a8938a7..e8bc267d4caf 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -410,13 +410,17 @@ ..() -/mob/living/launch_towards(datum/launch_metadata/LM) +/mob/living/launch_towards(list/launch_metadata) if(src) SEND_SIGNAL(src, COMSIG_MOB_MOVE_OR_LOOK, TRUE, dir, dir) - if(!istype(LM) || !LM.target || !src) + if(!ISSTRUCT(launch_metadata, launch_metadata) || !src) return + var/target = launch_metadata[PROP(launch_metadata, target)] + if(!target) + return + if(buckled) - LM.invoke_end_throw_callbacks(src) + invoke_end_throw_callbacks(launch_metadata, src) return if(pulling) stop_pulling() //being thrown breaks pulls. diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index b19afb0450cd..f041276e6cf4 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -39,10 +39,12 @@ dtype = W.damtype var/impact_damage = (1 + O.throwforce*THROWFORCE_COEFF)*O.throwforce*THROW_SPEED_IMPACT_COEFF*O.cur_speed - var/datum/launch_metadata/LM = O.launch_metadata + var/list/launch_metadata = O.launch_metadata var/dist = 2 - if(istype(LM)) - dist = LM.dist + var/thrower + if(ISSTRUCT(launch_metadata, launch_metadata)) + dist = launch_metadata[PROP(launch_metadata, dist)] + thrower = launch_metadata[PROP(launch_metadata, thrower)] var/miss_chance = min(15*(dist - 2), 0) if (prob(miss_chance)) @@ -64,31 +66,31 @@ O.throwing = 0 //it hit, so stop moving - var/mob/M - if(ismob(LM.thrower)) - M = LM.thrower + var/mob/mob_thrower + if(ismob(thrower)) + mob_thrower = thrower if(damage_done > 5) - M.track_hit(initial(O.name)) - if (M.faction == faction) - M.track_friendly_fire(initial(O.name)) - var/client/assailant = M.client + mob_thrower.track_hit(initial(O.name)) + if (mob_thrower.faction == faction) + mob_thrower.track_friendly_fire(initial(O.name)) + var/client/assailant = mob_thrower.client if(assailant) - src.attack_log += text("\[[time_stamp()]\] Has been hit with \a [O], thrown by [key_name(M)]") - M.attack_log += text("\[[time_stamp()]\] Hit [key_name(src)] with a thrown [O]") + src.attack_log += text("\[[time_stamp()]\] Has been hit with \a [O], thrown by [key_name(mob_thrower)]") + mob_thrower.attack_log += text("\[[time_stamp()]\] Hit [key_name(src)] with a thrown [O]") if(!istype(src,/mob/living/simple_animal/mouse)) if(src.loc) - 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 [O], 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) else - msg_admin_attack("[key_name(src)] was hit by \a [O], thrown by [key_name(M)] in [get_area(M)] ([M.loc.x],[M.loc.y],[M.loc.z]).", M.loc.x, M.loc.y, M.loc.z) + msg_admin_attack("[key_name(src)] was hit by \a [O], thrown by [key_name(mob_thrower)] in [get_area(mob_thrower)] ([mob_thrower.loc.x],[mob_thrower.loc.y],[mob_thrower.loc.z]).", mob_thrower.loc.x, mob_thrower.loc.y, mob_thrower.loc.z) if(last_damage_source) - last_damage_data = create_cause_data(last_damage_source, M) + last_damage_data = create_cause_data(last_damage_source, mob_thrower) /mob/living/mob_launch_collision(mob/living/L) L.Move(get_step_away(L, src)) /mob/living/obj_launch_collision(obj/O) - var/datum/launch_metadata/LM = launch_metadata - if(!rebounding && LM.thrower != src) + var/atom/thrower = launch_metadata[PROP(launch_metadata, thrower)] + if(!rebounding && thrower != src) var/impact_damage = (1 + MOB_SIZE_COEFF/(mob_size + 1))*THROW_SPEED_DENSE_COEFF*cur_speed apply_damage(impact_damage) visible_message(SPAN_DANGER("\The [name] slams into [O]!"), null, null, 5) //feedback to know that you got slammed into a wall and it hurt @@ -97,10 +99,10 @@ //This is called when the mob or human is thrown into a dense turf or wall /mob/living/turf_launch_collision(turf/T) - var/datum/launch_metadata/LM = launch_metadata - if(!rebounding && LM.thrower != src) - if(LM.thrower) - last_damage_data = create_cause_data("wall tossing", LM.thrower) + var/atom/thrower = launch_metadata[PROP(launch_metadata, thrower)] + if(!rebounding && thrower != src) + if(thrower) + last_damage_data = create_cause_data("wall tossing", thrower) var/impact_damage = (1 + MOB_SIZE_COEFF/(mob_size + 1))*THROW_SPEED_DENSE_COEFF*cur_speed apply_damage(impact_damage) visible_message(SPAN_DANGER("\The [name] slams into [T]!"), null, null, 5) //feedback to know that you got slammed into a wall and it hurt diff --git a/code/modules/movement/launching/launching.dm b/code/modules/movement/launching/launching.dm index 778c452a3240..e11595d39c35 100644 --- a/code/modules/movement/launching/launching.dm +++ b/code/modules/movement/launching/launching.dm @@ -1,36 +1,8 @@ -/datum/launch_metadata - // Parameters - var/pass_flags = NO_FLAGS // Pass flags to add temporarily - var/call_all = FALSE // Whether to perform all callback calls or none of them - - var/atom/target - var/range - var/speed - var/atom/thrower - var/spin - - // A list of callbacks to invoke when an atom of a specific type is hit (keys are typepaths and values are proc paths) - // These should only be for CUSTOM procs to invoke when an atom of a specific type is collided with, otherwise will default to using - // the appropriate mob/obj/turf collision procs - // The callbacks can be standard or dynamic, though dynamic callbacks can only be called by the atom being thrown - var/list/collision_callbacks - - /// A list of callbacks to invoke when the throw completes successfully - var/list/end_throw_callbacks - - // Tracked information - var/dist = 0 - -/datum/launch_metadata/Destroy(force, ...) - target = null - thrower = null - return ..() - - -/datum/launch_metadata/proc/get_collision_callbacks(atom/A) +/proc/get_collision_callbacks(list/launch_metadata, atom/A) var/highest_matching = null var/list/matching = list() + var/collision_callbacks = launch_metadata[PROP(launch_metadata, collision_callbacks)] if (isnull(collision_callbacks)) return null @@ -42,6 +14,7 @@ highest_matching = path matching += path + var/call_all = launch_metadata[PROP(launch_metadata, call_all)] if (!call_all) if (isnull(highest_matching)) return null @@ -56,7 +29,8 @@ /// Invoke end_throw_callbacks on this metadata. /// Takes argument of type /atom/movable -/datum/launch_metadata/proc/invoke_end_throw_callbacks(atom/movable/movable_atom) +/proc/invoke_end_throw_callbacks(list/launch_metadata, atom/movable/movable_atom) + var/end_throw_callbacks = launch_metadata[PROP(launch_metadata, end_throw_callbacks)] if(length(end_throw_callbacks)) for(var/datum/callback/callback as anything in end_throw_callbacks) if(istype(callback, /datum/callback/dynamic)) @@ -64,14 +38,16 @@ else callback.Invoke() -/atom/movable/var/datum/launch_metadata/launch_metadata = null +/// Struct containing information to process when a mob is thrown +/// and how impacting mobs react to it +/atom/movable/var/list/launch_metadata //called when src is thrown into hit_atom /atom/movable/proc/launch_impact(atom/hit_atom) if (isnull(launch_metadata)) CRASH("launch_impact called without any stored metadata") - var/list/collision_callbacks = launch_metadata.get_collision_callbacks(hit_atom) + var/list/collision_callbacks = get_collision_callbacks(launch_metadata, hit_atom) if (islist(collision_callbacks)) for(var/datum/callback/CB as anything in collision_callbacks) if(istype(CB, /datum/callback/dynamic)) @@ -117,14 +93,13 @@ /atom/movable/proc/rebound(oldloc, launched_speed) if (loc == oldloc) rebounding = TRUE - var/datum/launch_metadata/LM = new() - LM.target = get_step(src, turn(dir, 180)) - LM.range = 1 - LM.speed = launched_speed - LM.pass_flags = PASS_UNDER - LM.pass_flags |= (ismob(src) ? PASS_OVER_THROW_MOB : PASS_OVER_THROW_ITEM) + var/list/launch_metadata = STRUCT(launch_metadata) + launch_metadata[PROP(launch_metadata, target)] = get_step(src, turn(dir, 180)) + launch_metadata[PROP(launch_metadata, range)] = 1 + launch_metadata[PROP(launch_metadata, speed)] = launched_speed + launch_metadata[PROP(launch_metadata, pass_flags)] = PASS_UNDER|(ismob(src) ? PASS_OVER_THROW_MOB : PASS_OVER_THROW_ITEM) - launch_towards(LM) + launch_towards(launch_metadata) /atom/movable/proc/try_to_throw(mob/living/user) return TRUE @@ -138,68 +113,77 @@ if (HIGH_LAUNCH) temp_pass_flags |= PASS_HIGH_OVER - var/datum/launch_metadata/LM = new() - LM.pass_flags = temp_pass_flags - LM.target = target - LM.range = range - LM.speed = speed - LM.thrower = thrower - LM.spin = spin + var/list/launch_metadata = STRUCT(launch_metadata) + launch_metadata[PROP(launch_metadata, pass_flags)] = temp_pass_flags + launch_metadata[PROP(launch_metadata, target)] = target + launch_metadata[PROP(launch_metadata, range)] = range + launch_metadata[PROP(launch_metadata, speed)] = speed + launch_metadata[PROP(launch_metadata, thrower)] = thrower + launch_metadata[PROP(launch_metadata, spin)] = spin if(end_throw_callbacks) - LM.end_throw_callbacks = end_throw_callbacks + launch_metadata[PROP(launch_metadata, end_throw_callbacks)] = end_throw_callbacks if(collision_callbacks) - LM.collision_callbacks = collision_callbacks + launch_metadata[PROP(launch_metadata, collision_callbacks)] = collision_callbacks - if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_LAUNCH, LM) & COMPONENT_LAUNCH_CANCEL) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_LAUNCH, launch_metadata) & COMPONENT_LAUNCH_CANCEL) return - launch_towards(LM) + launch_towards(launch_metadata) // Proc for throwing or propelling movable atoms towards a target -/atom/movable/proc/launch_towards(datum/launch_metadata/LM) - if (!istype(LM)) +/atom/movable/proc/launch_towards(list/new_launch_metadata) + if (!ISSTRUCT(new_launch_metadata, launch_metadata)) CRASH("invalid launch_metadata passed to launch_towards") - if (!LM.target || !src) + + var/target = new_launch_metadata[PROP(launch_metadata, target)] + if (!target || !src) return - if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_THROW, LM.thrower) & COMPONENT_CANCEL_THROW) + var/thrower = new_launch_metadata[PROP(launch_metadata, thrower)] + if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_THROW, thrower) & COMPONENT_CANCEL_THROW) return // If we already have launch_metadata (from a previous throw), reset it and qdel the old launch_metadata datum - if (istype(launch_metadata)) - qdel(launch_metadata) - launch_metadata = LM + if (ISSTRUCT(launch_metadata, launch_metadata)) + QDEL_STRUCT(launch_metadata, launch_metadata) + launch_metadata = new_launch_metadata - if (LM.spin) - animation_spin(5, 1 + min(1, LM.range/20)) + var/spin = new_launch_metadata[PROP(launch_metadata, spin)] + if (spin) + var/range = new_launch_metadata[PROP(launch_metadata, spin)] + animation_spin(5, 1 + min(1, range/20)) var/old_speed = cur_speed - cur_speed = clamp(LM.speed, MIN_SPEED, MAX_SPEED) // Sanity check, also ~1 sec delay between each launch move is not very reasonable + var/metadata_speed = new_launch_metadata[PROP(launch_metadata, speed)] + cur_speed = clamp(metadata_speed, MIN_SPEED, MAX_SPEED) // Sanity check, also ~1 sec delay between each launch move is not very reasonable var/delay = 10/cur_speed - 0.5 // scales delay back to deciseconds for when sleep is called - var/pass_flags = LM.pass_flags + var/pass_flags = new_launch_metadata[PROP(launch_metadata, pass_flags)] throwing = TRUE add_temp_pass_flags(pass_flags) - var/turf/start_turf = get_step_towards(src, LM.target) - var/list/turf/path = get_line(start_turf, LM.target) + var/turf/start_turf = get_step_towards(src, target) + var/list/turf/path = get_line(start_turf, target) var/last_loc = loc var/early_exit = FALSE - LM.dist = 0 + launch_metadata[PROP(launch_metadata, dist)] = 0 for (var/turf/T in path) if (!src || !throwing || loc != last_loc || !isturf(src.loc)) break - if (!LM || QDELETED(LM)) + if (!ISSTRUCT(launch_metadata, launch_metadata)) early_exit = TRUE break - if (LM.dist >= LM.range) + var/dist = launch_metadata[PROP(launch_metadata, dist)] + var/range = launch_metadata[PROP(launch_metadata, range)] + if (dist >= range) break if (!Move(T)) // If this returns FALSE, then a collision happened break last_loc = loc - if (++LM.dist >= LM.range) + dist = ++launch_metadata[PROP(launch_metadata, dist)] + if (dist >= range) break sleep(delay) @@ -208,10 +192,10 @@ var/turf/T = get_turf(src) if(!istype(T)) return - var/atom/hit_atom = ismob(LM.target) ? null : T // TODO, just check for LM.target, the ismob is to prevent funky behavior with grenades 'n crates + var/atom/hit_atom = ismob(target) ? null : T // TODO, just check for LM.target, the ismob is to prevent funky behavior with grenades 'n crates if(!hit_atom) for(var/atom/A in T) - if(A == LM.target) + if(A == target) hit_atom = A break launch_impact(hit_atom) @@ -220,8 +204,8 @@ rebounding = FALSE cur_speed = old_speed remove_temp_pass_flags(pass_flags) - LM.invoke_end_throw_callbacks(src) - QDEL_NULL(launch_metadata) + invoke_end_throw_callbacks(new_launch_metadata, src) + QDEL_NULL_STRUCT(launch_metadata, launch_metadata) /atom/movable/proc/throw_random_direction(range, speed = 0, atom/thrower, spin, launch_type = NORMAL_LAUNCH, pass_flags = NO_FLAGS) var/throw_direction = pick(CARDINAL_ALL_DIRS) diff --git a/colonialmarines.dme b/colonialmarines.dme index 48996a93ec30..73b43309e6e7 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -28,6 +28,7 @@ #include "code\__DEFINES\_macros.dm" #include "code\__DEFINES\_math.dm" #include "code\__DEFINES\_protect.dm" +#include "code\__DEFINES\_struct.dm" #include "code\__DEFINES\_tick.dm" #include "code\__DEFINES\access.dm" #include "code\__DEFINES\admin.dm" @@ -202,6 +203,7 @@ #include "code\_globalvars\global_lists.dm" #include "code\_globalvars\misc.dm" #include "code\_globalvars\regexes.dm" +#include "code\_globalvars\struct.dm" #include "code\_globalvars\tgui.dm" #include "code\_globalvars\lists\clans.dm" #include "code\_globalvars\lists\client.dm" @@ -239,6 +241,7 @@ #include "code\_onclick\hud\rendering\plane_master_controller.dm" #include "code\_onclick\hud\rendering\render_plate.dm" #include "code\controllers\shuttle_controller.dm" +#include "code\controllers\struct.dm" #include "code\controllers\topic.dm" #include "code\controllers\configuration\config_entry.dm" #include "code\controllers\configuration\configuration.dm" @@ -672,6 +675,8 @@ #include "code\datums\status_effects\limited_effect.dm" #include "code\datums\status_effects\stacking_effect.dm" #include "code\datums\status_effects\debuffs\debuffs.dm" +#include "code\datums\structs\_struct.dm" +#include "code\datums\structs\launch_metadata.dm" #include "code\datums\supply_packs\_supply_packs.dm" #include "code\datums\supply_packs\ammo.dm" #include "code\datums\supply_packs\attachments.dm"