diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 8ca59caee70..866207201fa 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -442,6 +442,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define LEAPER_BUBBLE_TRAIT "leaper-bubble" #define STICKY_NODROP "sticky-nodrop" //sticky nodrop sounds like a bad soundcloud rapper's name #define SKILLCHIP_TRAIT "skillchip" +#define BUSY_FLOORBOT_TRAIT "busy-floorbot" #define PULLED_WHILE_SOFTCRIT_TRAIT "pulled-while-softcrit" #define LOCKED_BORG_TRAIT "locked-borg" #define LACKING_LOCOMOTION_APPENDAGES_TRAIT "lacking-locomotion-appengades" //trait associated to not having locomotion appendages nor the ability to fly or float diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 24cb7d83692..5393091e631 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -417,14 +417,15 @@ return FALSE return TRUE -/obj/item/stack/proc/merge(obj/item/stack/S) //Merge src into S, as much as possible +///Merges src into S, as much as possible. If present, the limit arg overrides S.max_amount for transfer. +/obj/item/stack/proc/merge(obj/item/stack/S, limit) if(QDELETED(S) || QDELETED(src) || S == src) //amusingly this can cause a stack to consume itself, let's not allow that. return var/transfer = get_amount() if(S.is_cyborg) transfer = min(transfer, round((S.source.max_energy - S.source.energy) / S.cost)) else - transfer = min(transfer, S.max_amount - S.amount) + transfer = min(transfer, (limit ? limit : S.max_amount) - S.amount) if(pulledby) pulledby.start_pulling(S) S.copy_evidences(src) diff --git a/code/game/objects/items/stacks/tiles/light.dm b/code/game/objects/items/stacks/tiles/light.dm index 9206be0b33d..506d1222ed7 100644 --- a/code/game/objects/items/stacks/tiles/light.dm +++ b/code/game/objects/items/stacks/tiles/light.dm @@ -19,3 +19,8 @@ qdel(src) else return ..() + +/obj/item/stack/tile/light/place_tile(turf/open/T) + . = ..() + var/turf/open/floor/light/F = . + F?.state = state diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index 2a0497d4315..4c5cd820216 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -94,6 +94,11 @@ else return ..() +/obj/item/stack/tile/proc/place_tile(turf/open/T) + if(!turf_type || !use(1)) + return + . = T.PlaceOnTop(turf_type, flags = CHANGETURF_INHERIT_AIR) + //Grass /obj/item/stack/tile/grass name = "grass tile" @@ -421,6 +426,11 @@ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS merge_type = /obj/item/stack/tile/material +/obj/item/stack/tile/material/place_tile(turf/open/T) + . = ..() + var/turf/open/floor/material/F = . + F?.set_custom_materials(mats_per_unit) + /obj/item/stack/tile/eighties name = "retro tile" singular_name = "retro floor tile" diff --git a/code/game/turfs/open/floor/plating.dm b/code/game/turfs/open/floor/plating.dm index 180bb7e2a4e..1235dde1c0c 100644 --- a/code/game/turfs/open/floor/plating.dm +++ b/code/game/turfs/open/floor/plating.dm @@ -66,19 +66,8 @@ for(var/M in O.buckled_mobs) to_chat(user, "Someone is buckled to \the [O]! Unbuckle [M] to move \him out of the way.") return - var/obj/item/stack/tile/W = C - if(!W.use(1)) - return - if(istype(W, /obj/item/stack/tile/material)) - var/turf/newturf = PlaceOnTop(/turf/open/floor/material, flags = CHANGETURF_INHERIT_AIR) - newturf.set_custom_materials(W.mats_per_unit) - else if(W.turf_type) - var/turf/open/floor/T = PlaceOnTop(W.turf_type, flags = CHANGETURF_INHERIT_AIR) - if(istype(W, /obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow - var/obj/item/stack/tile/light/L = W - var/turf/open/floor/light/F = T - F.state = L.state - + var/obj/item/stack/tile/tile = C + tile.place_tile(src) playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE) else if(!iscyborg(user)) diff --git a/code/game/turfs/open/floor/reinf_floor.dm b/code/game/turfs/open/floor/reinf_floor.dm index 37409b5b661..dfed384f0d1 100644 --- a/code/game/turfs/open/floor/reinf_floor.dm +++ b/code/game/turfs/open/floor/reinf_floor.dm @@ -27,7 +27,7 @@ /turf/open/floor/engine/make_plating(force = FALSE) if(force) - ..() + return ..() return //unplateable /turf/open/floor/engine/try_replace_tile(obj/item/stack/tile/T, mob/user, params) diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index 04d52d20f0c..d53710aeb2a 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -5,7 +5,6 @@ icon = 'icons/mob/aibots.dmi' icon_state = "floorbot0" density = FALSE - anchored = FALSE health = 25 maxHealth = 25 @@ -22,9 +21,8 @@ var/targetdirection var/replacetiles = FALSE var/placetiles = FALSE - var/specialtiles = 0 var/maxtiles = 100 - var/obj/item/stack/tile/tiletype + var/obj/item/stack/tile/tilestack var/fixfloors = TRUE var/autotile = FALSE var/max_targets = 50 @@ -53,6 +51,17 @@ health = 100 maxHealth = 100 +/mob/living/simple_animal/bot/floorbot/Exited(atom/movable/A, atom/newloc) + if(A == tilestack) + if(tilestack && tilestack.max_amount < tilestack.amount) //split the stack if it exceeds its normal max_amount + var/iterations = round(tilestack.amount/tilestack.max_amount) //round() without second arg floors the value + for(var/a in 1 to iterations) + if(a == iterations) + tilestack.split_stack(null, tilestack.amount - tilestack.max_amount) + else + tilestack.split_stack(null, tilestack.max_amount) + tilestack = null + /mob/living/simple_animal/bot/floorbot/turn_on() . = ..() update_icon() @@ -66,8 +75,7 @@ target = null oldloc = null ignore_list = list() - anchored = FALSE - update_icon() + toggle_magnet(FALSE) /mob/living/simple_animal/bot/floorbot/set_custom_texts() text_hack = "You corrupt [name]'s construction protocols." @@ -82,8 +90,8 @@ dat += "Status: [on ? "On" : "Off"]
" dat += "Maintenance panel panel is [open ? "opened" : "closed"]
" dat += "Special tiles: " - if(specialtiles) - dat += "Loaded \[[specialtiles]/[maxtiles]\]
" + if(tilestack) + dat += "Loaded \[[tilestack.amount]/[maxtiles]\]
" else dat += "None Loaded
" @@ -93,7 +101,7 @@ dat += "Place floor tiles: [placetiles ? "Yes" : "No"]
" dat += "Replace existing floor tiles with custom tiles: [replacetiles ? "Yes" : "No"]
" dat += "Repair damaged tiles and platings: [fixfloors ? "Yes" : "No"]
" - dat += "Traction Magnets: [anchored ? "Engaged" : "Disengaged"]
" + dat += "Traction Magnets: [HAS_TRAIT_FROM(src, TRAIT_IMMOBILIZED, BUSY_FLOORBOT_TRAIT) ? "Engaged" : "Disengaged"]
" dat += "Patrol Station: [auto_patrol ? "Yes" : "No"]
" var/bmode if(targetdirection) @@ -108,23 +116,25 @@ if(istype(W, /obj/item/stack/tile/plasteel)) to_chat(user, "The floorbot can produce normal tiles itself.") return - if(specialtiles && istype(W, /obj/item/stack/tile)) - var/obj/item/stack/tile/usedtile = W - if(usedtile.type != tiletype) - to_chat(user, "Different custom tiles are already inside the floorbot.") - return if(istype(W, /obj/item/stack/tile)) - if(specialtiles >= maxtiles) - return - var/obj/item/stack/tile/tiles = W //used only to get the amount - tiletype = W.type - var/loaded = min(maxtiles-specialtiles, tiles.amount) - tiles.use(loaded) - specialtiles += loaded - if(loaded > 0) - to_chat(user, "You load [loaded] tiles into the floorbot. It now contains [specialtiles] tiles.") + var/old_amount = tilestack ? tilestack.amount : 0 + var/obj/item/stack/tile/tiles = W + if(tilestack) + if(!tiles.can_merge(tilestack)) + to_chat(user, "Different custom tiles are already inside the floorbot.") + return + if(tilestack.amount >= maxtiles) + to_chat(user, "The floorbot can't hold any more custom tiles.") + return + tiles.merge(tilestack, maxtiles) else - to_chat(user, "You need at least one floor tile to put into [src]!") + if(tiles.amount > maxtiles) + tilestack = tilestack.split_stack(null, maxtiles) + else + tilestack = W + tilestack.forceMove(src) + to_chat(user, "You load [tilestack.amount - old_amount] tiles into the floorbot. It now contains [tilestack.amount] tiles.") + return else ..() @@ -134,6 +144,19 @@ if(user) to_chat(user, "[src] buzzes and beeps.") +///mobs should use move_resist instead of anchored. +/mob/living/simple_animal/bot/floorbot/proc/toggle_magnet(engage = TRUE, change_icon = TRUE) + if(engage) + ADD_TRAIT(src, TRAIT_IMMOBILIZED, BUSY_FLOORBOT_TRAIT) + move_resist = INFINITY + if(change_icon) + icon_state = "[toolbox_color]floorbot-c" + else + REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, BUSY_FLOORBOT_TRAIT) + move_resist = initial(move_resist) + if(change_icon) + update_icon() + /mob/living/simple_animal/bot/floorbot/Topic(href, href_list) if(..()) return TRUE @@ -147,11 +170,11 @@ fixfloors = !fixfloors if("autotile") autotile = !autotile - if("anchor") - set_anchored(!anchored) + if("magnet") + toggle_magnet(!HAS_TRAIT_FROM(src, TRAIT_IMMOBILIZED, BUSY_FLOORBOT_TRAIT), FALSE) if("eject") - if(specialtiles && tiletype != null) - empty_tiles() + if(tilestack) + tilestack.forceMove(drop_location()) if("linemode") var/setdir = input("Select construction direction:") as null|anything in list("north","east","south","west","disable") @@ -168,11 +191,6 @@ targetdirection = null update_controls() -/mob/living/simple_animal/bot/floorbot/proc/empty_tiles() - new tiletype(drop_location(), specialtiles) - specialtiles = 0 - tiletype = null - /mob/living/simple_animal/bot/floorbot/handle_automated_action() if(!..()) return @@ -204,7 +222,7 @@ process_type = FIX_TILE target = scan(/turf/open/floor) - if(!target && replacetiles && specialtiles > 0) //Replace a floor tile with custom tile + if(!target && replacetiles && tilestack) //Replace a floor tile with custom tile process_type = REPLACE_TILE //The target must be a tile. The floor must already have a floortile. target = scan(/turf/open/floor) @@ -234,9 +252,12 @@ repair(target) else if(emagged == 2 && isfloorturf(target)) var/turf/open/floor/F = target - set_anchored(TRUE) + toggle_magnet() mode = BOT_REPAIRING - F.ReplaceWithLattice() + if(isplatingturf(F)) + F.ReplaceWithLattice() + else + F.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) audible_message("[src] makes an excited booping sound.") addtimer(CALLBACK(src, .proc/go_idle), 0.5 SECONDS) path = list() @@ -263,7 +284,7 @@ oldloc = loc /mob/living/simple_animal/bot/floorbot/proc/go_idle() - anchored = FALSE + toggle_magnet(FALSE) mode = BOT_IDLE target = null @@ -278,15 +299,16 @@ /mob/living/simple_animal/bot/floorbot/process_scan(scan_target) var/result var/turf/open/floor/F + move_resist = initial(move_resist) switch(process_type) if(HULL_BREACH) //The most common job, patching breaches in the station's hull. if(is_hull_breach(scan_target)) //Ensure that the targeted space turf is actually part of the station, and not random space. result = scan_target - set_anchored(TRUE) //Prevent the floorbot being blown off-course while trying to reach a hull breach. + move_resist = INFINITY //Prevent the floorbot being blown off-course while trying to reach a hull breach. if(LINE_SPACE_MODE) //Space turfs in our chosen direction are considered. if(get_dir(src, scan_target) == targetdirection) result = scan_target - set_anchored(TRUE) + move_resist = INFINITY if(PLACE_TILE) F = scan_target if(isplatingturf(F)) //The floor must not already have a tile. @@ -321,55 +343,57 @@ else if(!isfloorturf(target_turf)) return if(isspaceturf(target_turf)) //If we are fixing an area not part of pure space, it is - set_anchored(TRUE) - icon_state = "[toolbox_color]floorbot-c" + toggle_magnet() visible_message("[targetdirection ? "[src] begins installing a bridge plating." : "[src] begins to repair the hole."] ") mode = BOT_REPAIRING - sleep(50) - if(mode == BOT_REPAIRING && src.loc == target_turf) + if(do_after(src, 50, target = target_turf) && mode == BOT_REPAIRING) if(autotile) //Build the floor and include a tile. - target_turf.PlaceOnTop(/turf/open/floor/plasteel, flags = CHANGETURF_INHERIT_AIR) + if(replacetiles && tilestack) + tilestack.place_tile(target_turf) + if(!tilestack) + speak("Requesting refill of custom floor tiles to continue replacing.") + else + target_turf.PlaceOnTop(/turf/open/floor/plasteel, flags = CHANGETURF_INHERIT_AIR) else //Build a hull plating without a floor tile. target_turf.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) else var/turf/open/floor/F = target_turf + var/success = FALSE + var/was_replacing = replacetiles - if(F.type != initial(tiletype.turf_type) && (F.broken || F.burnt || isplatingturf(F)) || F.type == (initial(tiletype.turf_type) && (F.broken || F.burnt))) - set_anchored(TRUE) - icon_state = "[toolbox_color]floorbot-c" + if(F.broken || F.burnt || isplatingturf(F)) + toggle_magnet() mode = BOT_REPAIRING - visible_message("[src] begins repairing the floor.") - sleep(50) - if(mode == BOT_REPAIRING && F && src.loc == F) - F.broken = FALSE - F.burnt = FALSE - F.PlaceOnTop(/turf/open/floor/plasteel, flags = CHANGETURF_INHERIT_AIR) + visible_message("[src] begins [(F.broken || F.burnt) ? "repairing the floor" : "placing a floor tile"].") + if(do_after(src, 50, target = F) && mode == BOT_REPAIRING) + success = TRUE - if(replacetiles && F.type != initial(tiletype.turf_type) && specialtiles && !isplatingturf(F)) - set_anchored(TRUE) - icon_state = "[toolbox_color]floorbot-c" + else if(replacetiles && tilestack && F.type != tilestack.turf_type) + toggle_magnet() mode = BOT_REPAIRING visible_message("[src] begins replacing the floor tiles.") - sleep(50) - if(mode == BOT_REPAIRING && F && loc == F && replacetiles && specialtiles) //make sure our mode and tiles are the same after sleeping. - F.broken = FALSE - F.burnt = FALSE - F.PlaceOnTop(initial(tiletype.turf_type), flags = CHANGETURF_INHERIT_AIR) - specialtiles-- - if(specialtiles == 0) + if(do_after(src, 50, target = target_turf) && mode == BOT_REPAIRING && tilestack) + success = TRUE + + if(success) + F = F.make_plating(TRUE) || F + if(was_replacing && tilestack) + tilestack.place_tile(F) + if(!tilestack) speak("Requesting refill of custom floor tiles to continue replacing.") - mode = BOT_IDLE - update_icon() - anchored = FALSE - target = null + else + F.PlaceOnTop(/turf/open/floor/plasteel, flags = CHANGETURF_INHERIT_AIR) + + if(!QDELETED(src)) + go_idle() /mob/living/simple_animal/bot/floorbot/update_icon() icon_state = "[toolbox_color]floorbot[on]" - /mob/living/simple_animal/bot/floorbot/explode() on = FALSE + target = null visible_message("[src] blows apart!") var/atom/Tsec = drop_location() @@ -377,8 +401,8 @@ new /obj/item/assembly/prox_sensor(Tsec) - if(specialtiles && tiletype != null) - empty_tiles() + if(tilestack) + tilestack.forceMove(drop_location()) if(prob(50)) drop_part(robot_arm, Tsec)