mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-24 05:30:05 +01:00
[MIRROR] Adds the New-and-Improved Training Machine, Training Toolbox! (#1345)
* Adds the New-and-Improved Training Machine, Training Toolbox! (#54133) Co-authored-by: Jared-Fogle <35135081+Jared-Fogle@ users.noreply.github.com> Co-authored-by: ATH1909 <42606352+ATH1909@ users.noreply.github.com> Co-authored-by: Rohesie <rohesie@ gmail.com> * Adds the New-and-Improved Training Machine, Training Toolbox! Co-authored-by: Emmanuel S <mrdoomboyo@gmail.com> Co-authored-by: Jared-Fogle <35135081+Jared-Fogle@ users.noreply.github.com> Co-authored-by: ATH1909 <42606352+ATH1909@ users.noreply.github.com> Co-authored-by: Rohesie <rohesie@ gmail.com>
This commit is contained in:
co-authored by
Jared-Fogle
ATH1909
Rohesie
Emmanuel S
parent
b13fdd5ae4
commit
717c31d40c
@@ -1,76 +0,0 @@
|
||||
/obj/structure/target_stake
|
||||
name = "target stake"
|
||||
desc = "A thin platform with negatively-magnetized wheels."
|
||||
icon = 'icons/obj/objects.dmi'
|
||||
icon_state = "target_stake"
|
||||
density = FALSE
|
||||
flags_1 = CONDUCT_1
|
||||
can_buckle = TRUE
|
||||
max_buckled_mobs = 1
|
||||
buckle_lying = 0
|
||||
var/obj/item/target/pinned_target
|
||||
|
||||
/obj/structure/target_stake/Destroy()
|
||||
if(pinned_target)
|
||||
pinned_target.nullPinnedLoc()
|
||||
return ..()
|
||||
|
||||
/obj/structure/target_stake/proc/handle_density()
|
||||
if(length(buckled_mobs) || pinned_target)
|
||||
density = TRUE
|
||||
else
|
||||
density = FALSE
|
||||
|
||||
/obj/structure/target_stake/post_buckle_mob()
|
||||
handle_density()
|
||||
return ..()
|
||||
|
||||
/obj/structure/target_stake/post_unbuckle_mob()
|
||||
handle_density()
|
||||
return ..()
|
||||
|
||||
/obj/structure/target_stake/proc/nullPinnedTarget()
|
||||
pinned_target = null
|
||||
|
||||
/obj/structure/target_stake/Move()
|
||||
. = ..()
|
||||
if(pinned_target)
|
||||
pinned_target.forceMove(loc)
|
||||
|
||||
/obj/structure/target_stake/attackby(obj/item/target/T, mob/user)
|
||||
if(pinned_target)
|
||||
return
|
||||
if(istype(T) && user.transferItemToLoc(T, drop_location()))
|
||||
pinned_target = T
|
||||
T.pinnedLoc = src
|
||||
T.density = TRUE
|
||||
T.layer = OBJ_LAYER + 0.01
|
||||
handle_density()
|
||||
to_chat(user, "<span class='notice'>You slide the target into the stake.</span>")
|
||||
|
||||
/obj/structure/target_stake/attack_hand(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(pinned_target)
|
||||
removeTarget(user)
|
||||
|
||||
/obj/structure/target_stake/proc/removeTarget(mob/user)
|
||||
pinned_target.layer = OBJ_LAYER
|
||||
pinned_target.forceMove(user.loc)
|
||||
pinned_target.nullPinnedLoc()
|
||||
nullPinnedTarget()
|
||||
handle_density()
|
||||
if(ishuman(user))
|
||||
if(!user.get_active_held_item())
|
||||
user.put_in_hands(pinned_target)
|
||||
to_chat(user, "<span class='notice'>You take the target out of the stake.</span>")
|
||||
else
|
||||
pinned_target.forceMove(user.drop_location())
|
||||
to_chat(user, "<span class='notice'>You take the target out of the stake.</span>")
|
||||
|
||||
/obj/structure/target_stake/bullet_act(obj/projectile/P)
|
||||
if(pinned_target)
|
||||
pinned_target.bullet_act(P)
|
||||
else
|
||||
. = ..()
|
||||
@@ -0,0 +1,409 @@
|
||||
#define MIN_RANGE 1
|
||||
#define MIN_SPEED 1
|
||||
#define MAX_RANGE 7
|
||||
#define MAX_SPEED 10
|
||||
#define HITS_TO_KILL 9
|
||||
#define MIN_ATTACK_DELAY 10
|
||||
#define MAX_ATTACK_DELAY 15
|
||||
|
||||
/**
|
||||
* Machine that runs around wildly so people can practice clickin on things
|
||||
*
|
||||
* Can have a mob buckled on or a obj/item/target attached. Movement controlled by SSFastProcess,
|
||||
* movespeed controlled by cooldown macros. Can attach obj/item/target, obj/item/training_toolbox, and can buckle mobs to this.
|
||||
*/
|
||||
/obj/structure/training_machine
|
||||
name = "AURUMILL-Brand MkII. Personnel Training Machine"
|
||||
desc = "Used for combat training simulations. Accepts standard training targets. A pair of buckling straps are attached."
|
||||
icon = 'icons/obj/objects.dmi'
|
||||
icon_state = "training_machine"
|
||||
can_buckle = TRUE
|
||||
buckle_lying = 0
|
||||
max_integrity = 200
|
||||
///Is the machine moving? Setting this to FALSE will automatically call stop_moving()
|
||||
var/moving = FALSE
|
||||
///The distance the machine is allowed to roam from its starting point
|
||||
var/range = 1
|
||||
///A random spot within range that the trainer is trying to reach
|
||||
var/turf/target_position
|
||||
///The turf the machine was on when it was activated
|
||||
var/turf/starting_turf
|
||||
///Delay between process() calls. Cannot be higher than MAX_SPEED. Smaller value represents faster movement.
|
||||
var/move_speed = 1
|
||||
///Reference to a potentially attached object, either a target, trainer toolbox, or syndicate toolbox
|
||||
var/obj/attached_item
|
||||
///Helper for timing attacks when emagged
|
||||
COOLDOWN_DECLARE(attack_cooldown)
|
||||
///Cooldown macro to control how fast this will move. Used in process()
|
||||
COOLDOWN_DECLARE(move_cooldown)
|
||||
|
||||
/**
|
||||
* Called on qdel(), so we don't want a cool explosion to happen
|
||||
*/
|
||||
/obj/structure/training_machine/Destroy()
|
||||
remove_attached_item()
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Called on a normal destruction, so we have a cool explosion and toss whatever's attached
|
||||
*/
|
||||
/obj/structure/training_machine/obj_destruction(damage_flag)
|
||||
remove_attached_item(throwing = TRUE)
|
||||
explosion(src, 0,0,1, flame_range = 2)
|
||||
return ..()
|
||||
|
||||
/obj/structure/training_machine/ui_state(mob/user)
|
||||
return GLOB.physical_state
|
||||
|
||||
/obj/structure/training_machine/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "TrainingMachine", name)
|
||||
ui.open()
|
||||
|
||||
/**
|
||||
* Send data to the UI
|
||||
*
|
||||
* Include's the machine's movement range, speed, and whether or not it's active
|
||||
*/
|
||||
/obj/structure/training_machine/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["range"] = range
|
||||
data["movespeed"] = move_speed
|
||||
data["moving"] = moving
|
||||
return data
|
||||
|
||||
/**
|
||||
* Control the attached variables.
|
||||
*
|
||||
* Will not respond if moving and emagged, so once you set it to go it can't be stopped!
|
||||
*/
|
||||
/obj/structure/training_machine/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if (moving && obj_flags & EMAGGED)
|
||||
visible_message("<span class='warning'>The [src]'s control panel fizzles slightly.</span>")
|
||||
return
|
||||
switch(action)
|
||||
if("toggle")
|
||||
toggle()
|
||||
. = TRUE
|
||||
if("range")
|
||||
var/range_input = params["range"]
|
||||
range = clamp(range_input, MIN_RANGE, MAX_RANGE)
|
||||
. = TRUE
|
||||
if("movespeed")
|
||||
var/range_input = params["movespeed"]
|
||||
move_speed = clamp(range_input, MIN_SPEED, MAX_SPEED)
|
||||
. = TRUE
|
||||
|
||||
/obj/structure/training_machine/attack_hand(mob/user)
|
||||
ui_interact(user)
|
||||
|
||||
/**
|
||||
* Called when the machien is attacked by something
|
||||
*
|
||||
* Meant for attaching an item to the machine, should only be a training toolbox or target. If emagged, the
|
||||
* machine will gain an auto-attached syndicate toolbox, so in that case we shouldn't be able to swap it out
|
||||
*/
|
||||
/obj/structure/training_machine/attackby(obj/item/target, mob/user)
|
||||
if (user.a_intent != INTENT_HELP)
|
||||
return ..()
|
||||
if (!istype(target, /obj/item/training_toolbox) && !istype(target, /obj/item/target))
|
||||
return ..()
|
||||
if (obj_flags & EMAGGED)
|
||||
to_chat(user, "<span class='warning'>The toolbox is somehow stuck on! It won't budge!</span>")
|
||||
return
|
||||
attach_item(target)
|
||||
to_chat(user, "<span class='notice'>You attach \the [attached_item] to the training device.</span>")
|
||||
playsound(src, "rustle", 50, TRUE)
|
||||
|
||||
/**
|
||||
* Attach an item to the machine
|
||||
*
|
||||
* This proc technically works with any obj. Currently is only used for objects of type item/target and item/training_toolbox
|
||||
* Will make the attached item appear visually on the machine
|
||||
* Arguments
|
||||
* * target - The object to attach
|
||||
*/
|
||||
/obj/structure/training_machine/proc/attach_item(obj/target)
|
||||
remove_attached_item()
|
||||
attached_item = target
|
||||
attached_item.forceMove(src)
|
||||
attached_item.vis_flags |= VIS_INHERIT_ID
|
||||
vis_contents += attached_item
|
||||
RegisterSignal(attached_item, COMSIG_PARENT_QDELETING, .proc/on_attached_delete)
|
||||
handle_density()
|
||||
|
||||
/**
|
||||
* Called when the attached item is deleted.
|
||||
*
|
||||
* Cleans up behavior for when the attached item is deleted or removed.
|
||||
*/
|
||||
/obj/structure/training_machine/proc/on_attached_delete()
|
||||
UnregisterSignal(attached_item, COMSIG_PARENT_QDELETING)
|
||||
vis_contents -= attached_item
|
||||
attached_item = null
|
||||
handle_density()
|
||||
|
||||
/**
|
||||
* Remove the attached item from the machine
|
||||
*
|
||||
* Called when a user removes the item by hand or by swapping it out with another, when the machine breaks, or
|
||||
* when the machine is emagged.
|
||||
* Arguments
|
||||
* * user - The peson , if any, removing the attached item
|
||||
* * throwing - If we should make the item fly off the machine
|
||||
*/
|
||||
/obj/structure/training_machine/proc/remove_attached_item(mob/user, throwing = FALSE)
|
||||
if (!attached_item)
|
||||
return
|
||||
if (istype(attached_item, /obj/item/storage/toolbox/syndicate))
|
||||
UnregisterSignal(attached_item, COMSIG_PARENT_QDELETING)
|
||||
qdel(attached_item)
|
||||
else if (user)
|
||||
user.put_in_hands(attached_item)
|
||||
else
|
||||
attached_item.forceMove(drop_location())
|
||||
if (throwing && !QDELETED(attached_item)) //Fun little thing where we throw out the old attached item when emagged
|
||||
//We do a QDELETED check here because we don't want to throw the syndi toolbox, if it exists
|
||||
var/destination = get_edge_target_turf(get_turf(src), pick(GLOB.alldirs))
|
||||
attached_item.throw_at(destination, 4, 1)
|
||||
on_attached_delete()
|
||||
|
||||
/obj/structure/training_machine/AltClick(mob/user)
|
||||
. = ..()
|
||||
if (!attached_item)
|
||||
return
|
||||
if (obj_flags & EMAGGED)
|
||||
to_chat(user, "<span class='warning'>The toolbox is somehow stuck on! It won't budge!</span>")
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You remove \the [attached_item] from the training device.</span>")
|
||||
remove_attached_item(user)
|
||||
playsound(src, "rustle", 50, TRUE)
|
||||
|
||||
/**
|
||||
* Toggle the machine's movement
|
||||
*/
|
||||
/obj/structure/training_machine/proc/toggle()
|
||||
if (moving)
|
||||
stop_moving()
|
||||
else
|
||||
start_moving()
|
||||
|
||||
/**
|
||||
* Stop the machine's movement
|
||||
*
|
||||
* Will call STOP_PROCESSING, play a sound, and say an appropriate message
|
||||
* Arguments
|
||||
* * Message - the message the machine says when stopping
|
||||
*/
|
||||
/obj/structure/training_machine/proc/stop_moving(message = "Ending training simulation.")
|
||||
moving = FALSE
|
||||
starting_turf = null
|
||||
say(message)
|
||||
playsound(src,'sound/machines/synth_no.ogg',50,FALSE)
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/**
|
||||
* Start the machine's movement
|
||||
*
|
||||
* Says a message, plays a sound, then starts processing
|
||||
*/
|
||||
/obj/structure/training_machine/proc/start_moving()
|
||||
moving = TRUE
|
||||
starting_turf = get_turf(src)
|
||||
say("Beginning training simulation.")
|
||||
playsound(src,'sound/machines/triple_beep.ogg',50,FALSE)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/**
|
||||
* Main movement method for the machine
|
||||
*
|
||||
* Handles movement using SSFastProcess. Moves randomly, point-to-point, in an area centered around wherever it started.
|
||||
* Will only move if the move_cooldown cooldown macro is finished.
|
||||
* If it can't find a place to go, it will stop moving.
|
||||
*/
|
||||
/obj/structure/training_machine/process()
|
||||
if(!COOLDOWN_FINISHED(src, move_cooldown))
|
||||
return
|
||||
var/turf/current_turf = get_turf(src)
|
||||
if (!moving || !starting_turf || isspaceturf(current_turf))
|
||||
stop_moving()
|
||||
return
|
||||
if (current_turf == target_position) //We've reached our target turf, now find a new one
|
||||
target_position = null
|
||||
if (!target_position)
|
||||
target_position = find_target_position()
|
||||
if (!target_position)
|
||||
stop_moving("ERROR! Cannot calculate suitable movement path.")
|
||||
var/turf/nextStep = get_step_towards(src, target_position)
|
||||
if (!Move(nextStep, get_dir(src, nextStep)))
|
||||
target_position = null //We couldn't move towards the target turf, so find a new target turf
|
||||
try_attack()
|
||||
COOLDOWN_START(src, move_cooldown, max(MAX_SPEED - move_speed, 1))
|
||||
|
||||
/**
|
||||
* Find a suitable turf to move towards
|
||||
*/
|
||||
/obj/structure/training_machine/proc/find_target_position()
|
||||
var/list/turfs = list()
|
||||
for(var/turf/potential_turf in view(range, starting_turf))
|
||||
if (potential_turf.is_blocked_turf() || potential_turf == target_position)
|
||||
continue
|
||||
turfs += potential_turf
|
||||
if (!length(turfs))
|
||||
return
|
||||
return pick(turfs)
|
||||
|
||||
/**
|
||||
* Try to attack a nearby mob
|
||||
*
|
||||
* Called whenever the machine moves, this will look for mobs adjacent to the machine to attack.
|
||||
* Will attack with either a training toolbox (if attached), or a much more deadly syndicate toolbox (if emagged).
|
||||
* A cooldown macro (attack_cooldown) ensures it doesn't attack too quickly
|
||||
*/
|
||||
/obj/structure/training_machine/proc/try_attack()
|
||||
if (!attached_item || istype(attached_item, /obj/item/target))
|
||||
return
|
||||
if (!COOLDOWN_FINISHED(src, attack_cooldown))
|
||||
return
|
||||
var/list/targets
|
||||
for(var/mob/living/carbon/target in oview(1, get_turf(src))) //Find adjacent target
|
||||
if (target.stat == CONSCIOUS && target.Adjacent(src))
|
||||
LAZYADD(targets, target)
|
||||
var/mob/living/carbon/target = pick(targets)
|
||||
if (!target)
|
||||
return
|
||||
do_attack_animation(target, null, attached_item)
|
||||
if (obj_flags & EMAGGED)
|
||||
target.apply_damage(attached_item.force, BRUTE, BODY_ZONE_CHEST)
|
||||
playsound(src, 'sound/weapons/smash.ogg', 15, TRUE)
|
||||
COOLDOWN_START(src, attack_cooldown, rand(MIN_ATTACK_DELAY, MAX_ATTACK_DELAY))
|
||||
|
||||
/**
|
||||
* Make sure the machine can't be walked through if something is attached
|
||||
*/
|
||||
/obj/structure/training_machine/proc/handle_density()
|
||||
if(length(buckled_mobs) || attached_item)
|
||||
density = TRUE
|
||||
else
|
||||
density = FALSE
|
||||
|
||||
/obj/structure/training_machine/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE)
|
||||
. = ..()
|
||||
if (istype(attached_item, /obj/item/target))
|
||||
return FALSE
|
||||
|
||||
/obj/structure/training_machine/post_buckle_mob()
|
||||
handle_density()
|
||||
return ..()
|
||||
|
||||
/obj/structure/training_machine/post_unbuckle_mob()
|
||||
handle_density()
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Emagging causes a deadly, unremovable syndicate toolbox to be attached to the machine
|
||||
*/
|
||||
/obj/structure/training_machine/emag_act(mob/user)
|
||||
. = ..()
|
||||
if (obj_flags & EMAGGED)
|
||||
return
|
||||
obj_flags |= EMAGGED
|
||||
remove_attached_item(throwing = TRUE) //Toss out the old attached item!
|
||||
attach_item(new /obj/item/storage/toolbox/syndicate(src))
|
||||
to_chat(user, "<span class='warning'>You override the training machine's safety protocols, and activate its realistic combat feature. A toolbox pops out of a slot on the top.</span>")
|
||||
playsound(src, 'sound/machines/click.ogg', 50, TRUE)
|
||||
add_overlay("evil_trainer")
|
||||
|
||||
/obj/structure/training_machine/examine(mob/user)
|
||||
. = ..()
|
||||
if (obj_flags & EMAGGED)
|
||||
. += "<span class='warning'>It has a dangerous-looking toolbox attached to it, and the control panel is smoking sightly...</span>"
|
||||
else if (attached_item) //Can't removed the syndicate toolbox!
|
||||
. += "<span class='notice'><b>Alt-Click to remove \the [attached_item]</b></span>"
|
||||
. += "<span class='notice'><b>Click to open control interface.</b></span>"
|
||||
|
||||
/**
|
||||
* Device that simply counts the number of times you've hit a mob or target with. Looks like a toolbox but isn't.
|
||||
*
|
||||
* Also has a 'Lap' function for keeping track of hits made at a certain point. Also, looks kinda like his grace for laughs and pranks.
|
||||
*/
|
||||
/obj/item/training_toolbox
|
||||
name = "Training Toolbox"
|
||||
desc = "AURUMILL-Brand Baby's First Training Toolbox. A digital display on the back keeps track of hits made by the user. Second toolbox sold seperately!"
|
||||
icon_state = "his_grace_ascended"
|
||||
inhand_icon_state = "toolbox_gold"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/toolbox_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/toolbox_righthand.dmi'
|
||||
flags_1 = CONDUCT_1
|
||||
force = 0
|
||||
throwforce = 0
|
||||
throw_speed = 2
|
||||
throw_range = 7
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
///Total number of hits made against a valid target
|
||||
var/total_hits = 0
|
||||
///Number of hits made since the Lap button (alt-click) was last pushed
|
||||
var/lap_hits = 0
|
||||
|
||||
/obj/item/training_toolbox/afterattack(atom/target, mob/user, proximity)
|
||||
. = ..()
|
||||
if (!proximity || target == user || user.a_intent == INTENT_HELP)
|
||||
return
|
||||
if (check_hit(target))
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
|
||||
/**
|
||||
* Check if we should increment the hit counter
|
||||
*
|
||||
* Increments the 'hit' counter if the target we're attacking is a mob, target, or training machine with an attached item.
|
||||
* Will beep every 9 hits, as 9 hits usually signifies a KO with a normal toolbox
|
||||
* Arguments
|
||||
* * target - the atom we're hitting
|
||||
*/
|
||||
/obj/item/training_toolbox/proc/check_hit(atom/target)
|
||||
var/target_is_machine = istype(target, /obj/structure/training_machine)
|
||||
if (!ismob(target) && !istype(target, /obj/item/target) && !target_is_machine)
|
||||
return FALSE
|
||||
if (target_is_machine)
|
||||
var/obj/structure/training_machine/trainer = target
|
||||
if (!trainer.attached_item)
|
||||
return FALSE
|
||||
total_hits++
|
||||
lap_hits++
|
||||
playsound(src,'sound/weapons/smash.ogg',50,FALSE)
|
||||
if (lap_hits % HITS_TO_KILL == 0)
|
||||
playsound(src,'sound/machines/twobeep.ogg',25,FALSE)
|
||||
return TRUE
|
||||
|
||||
/obj/item/training_toolbox/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
||||
. = ..()
|
||||
if (!.)
|
||||
check_hit(hit_atom)
|
||||
|
||||
/obj/item/training_toolbox/AltClick(mob/user)
|
||||
. = ..()
|
||||
to_chat(user, "<span class='notice'>You push the 'Lap' button on the toolbox's display.</span>")
|
||||
lap_hits = initial(lap_hits)
|
||||
|
||||
/obj/item/training_toolbox/examine(mob/user)
|
||||
. = ..()
|
||||
if(!in_range(src, user) && !isobserver(user))
|
||||
. += "<span class='notice'>You can see a display on the back. You'll need to get closer to read it, though.</span>"
|
||||
return
|
||||
. += "<span class='notice'>A display on the back reads:</span>"
|
||||
. += "<span class='notice'>Total Hits: <b>[total_hits]</b></span>"
|
||||
if (lap_hits != total_hits)
|
||||
. += "<span class='notice'>Current Lap: <b>[lap_hits]</b></span>"
|
||||
. += "<span class='notice'><b>Alt-Click to 'Lap' the hit counter.</b></span>"
|
||||
|
||||
#undef MIN_RANGE
|
||||
#undef MIN_SPEED
|
||||
#undef MAX_RANGE
|
||||
#undef MAX_SPEED
|
||||
#undef HITS_TO_KILL
|
||||
#undef MIN_ATTACK_DELAY
|
||||
#undef MAX_ATTACK_DELAY
|
||||
Reference in New Issue
Block a user