diff --git a/code/LINDA/LINDA_turf_tile.dm b/code/LINDA/LINDA_turf_tile.dm
index 30f9be83b4c..b36c5ed82cc 100644
--- a/code/LINDA/LINDA_turf_tile.dm
+++ b/code/LINDA/LINDA_turf_tile.dm
@@ -46,7 +46,8 @@
var/datum/gas_mixture/air
var/archived_cycle = 0
var/current_cycle = 0
-
+ var/icy = 0
+ var/icyoverlay
var/obj/effect/hotspot/active_hotspot
var/temperature_archived //USED ONLY FOR SOLIDS
@@ -67,6 +68,8 @@
air.temperature = temperature
+ update_visuals()
+
/turf/simulated/Destroy()
visibilityChanged()
if(active_hotspot)
@@ -209,8 +212,6 @@
air.react()
- update_visuals()
-
if(air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
hotspot_expose(air.temperature, CELL_VOLUME)
for(var/atom/movable/item in src)
@@ -221,6 +222,13 @@
if(consider_superconductivity(starting = 1))
remove = 0
+ if(air.temperature < T0C && air.return_pressure() > 10)
+ icy = 1
+ else
+ icy = 0
+
+ update_visuals()
+
if(!excited_group && remove == 1)
air_master.remove_from_active(src)
@@ -232,6 +240,13 @@
archived_cycle = air_master.current_cycle
/turf/simulated/proc/update_visuals()
+ if(icy && !icyoverlay)
+ overlays |= icemaster
+ icyoverlay = icemaster
+ else if(icyoverlay && !icy)
+ icyoverlay = null
+ overlays -= icemaster
+
var/new_overlay_type = tile_graphic()
if (new_overlay_type == atmos_overlay_type)
return
diff --git a/code/__DEFINES/hydroponics.dm b/code/__DEFINES/hydroponics.dm
index a53e66f0a0f..f6693b0bcfc 100644
--- a/code/__DEFINES/hydroponics.dm
+++ b/code/__DEFINES/hydroponics.dm
@@ -56,4 +56,5 @@
#define TRAIT_BIOLUM 36
#define TRAIT_BIOLUM_COLOUR 37
#define TRAIT_IMMUTABLE 38
-#define TRAIT_RARITY 39
\ No newline at end of file
+#define TRAIT_RARITY 39
+#define TRAIT_BATTERY_RECHARGE 40
\ No newline at end of file
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index fe27e399c84..cc6d6f9c621 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -1,20 +1,38 @@
-/proc/random_underwear(var/gender)
+proc/random_underwear(gender, species = "Human")
+ var/list/pick_list = list()
switch(gender)
- if(MALE) return pick(underwear_m)
- if(FEMALE) return pick(underwear_f)
- else return pick(underwear_list)
+ if(MALE) pick_list = underwear_m
+ if(FEMALE) pick_list = underwear_f
+ else pick_list = underwear_list
+ return pick_species_allowed_underwear(pick_list, species)
-/proc/random_undershirt(var/gender)
+proc/random_undershirt(gender, species = "Human")
+ var/list/pick_list = list()
switch(gender)
- if(MALE) return pick(undershirt_m)
- if(FEMALE) return pick(undershirt_f)
- else return pick(undershirt_list)
+ if(MALE) pick_list = undershirt_m
+ if(FEMALE) pick_list = undershirt_f
+ else pick_list = undershirt_list
+ return pick_species_allowed_underwear(pick_list, species)
-/proc/random_socks(gender)
+proc/random_socks(gender, species = "Human")
+ var/list/pick_list = list()
switch(gender)
- if(MALE) return pick(socks_m)
- if(FEMALE) return pick(socks_f)
- else return pick(socks_list)
+ if(MALE) pick_list = socks_m
+ if(FEMALE) pick_list = socks_f
+ else pick_list = socks_list
+ return pick_species_allowed_underwear(pick_list, species)
+
+proc/pick_species_allowed_underwear(list/all_picks, species)
+ var/list/valid_picks = list()
+ for(var/test in all_picks)
+ var/datum/sprite_accessory/S = all_picks[test]
+ if(!(species in S.species_allowed))
+ continue
+ valid_picks += test
+
+ if(!valid_picks.len) valid_picks += "Nude"
+
+ return pick(valid_picks)
proc/random_hair_style(var/gender, species = "Human")
var/h_style = "Bald"
@@ -35,7 +53,7 @@ proc/random_hair_style(var/gender, species = "Human")
return h_style
-/proc/GetOppositeDir(var/dir)
+proc/GetOppositeDir(var/dir)
switch(dir)
if(NORTH) return SOUTH
if(SOUTH) return NORTH
diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm
index bb9d4f354eb..51e0dfa8ab2 100644
--- a/code/_globalvars/misc.dm
+++ b/code/_globalvars/misc.dm
@@ -1,5 +1,6 @@
var/global/obj/effect/overlay/plmaster = null
var/global/obj/effect/overlay/slmaster = null
+var/global/obj/effect/overlay/icemaster = null
// nanomanager, the manager for Nano UIs
var/datum/nanomanager/nanomanager = new()
diff --git a/code/controllers/Processes/air.dm b/code/controllers/Processes/air.dm
index a20169c86d3..df52e295bcf 100644
--- a/code/controllers/Processes/air.dm
+++ b/code/controllers/Processes/air.dm
@@ -140,4 +140,10 @@ var/global/datum/controller/process/air_system/air_master
slmaster.icon = 'icons/effects/tile_effects.dmi'
slmaster.icon_state = "sleeping_agent"
slmaster.layer = FLY_LAYER
- slmaster.mouse_opacity = 0
\ No newline at end of file
+ slmaster.mouse_opacity = 0
+
+ icemaster = new /obj/effect/overlay()
+ icemaster.icon = 'icons/turf/overlays.dmi'
+ icemaster.icon_state = "snowfloor"
+ icemaster.layer = TURF_LAYER+0.1
+ icemaster.mouse_opacity = 0
\ No newline at end of file
diff --git a/code/datums/cargoprofile.dm b/code/datums/cargoprofile.dm
index 9fcb356b9b1..cbb074b8d24 100644
--- a/code/datums/cargoprofile.dm
+++ b/code/datums/cargoprofile.dm
@@ -258,7 +258,7 @@
whitelist = list(/obj/item/weapon/banhammer,/obj/item/weapon/sord,/obj/item/weapon/butch,/obj/item/weapon/claymore,/obj/item/weapon/holo/esword,
/obj/item/weapon/flamethrower,/obj/item/weapon/grenade,/obj/item/weapon/gun,/obj/item/weapon/hatchet,/obj/item/weapon/katana,
/obj/item/weapon/kitchenknife,/obj/item/weapon/melee,/obj/item/weapon/nullrod,/obj/item/weapon/pickaxe,/obj/item/weapon/twohanded,
- /obj/item/weapon/c4,/obj/item/weapon/scalpel,/obj/item/weapon/shield,/obj/item/weapon/grown/deathnettle)
+ /obj/item/weapon/c4,/obj/item/weapon/scalpel,/obj/item/weapon/shield,/obj/item/weapon/grown/nettle/death)
/datum/cargoprofile/tools
name = "Devices & Tools"
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index 65e6491bb0d..89f4a277796 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -592,7 +592,7 @@ client
if(!istype(H))
usr << "This can only be used on instances of type /mob/living/carbon/human"
return
-
+
var/confirm = alert("Are you sure you want to turn this mob into a skeleton?","Confirm Skeleton Transformation","Yes","No")
if(confirm != "Yes")
return
@@ -601,7 +601,7 @@ client
message_admins("[key_name(usr)] has turned [key_name(H)] into a skeleton")
log_admin("[key_name_admin(usr)] has turned [key_name_admin(H)] into a skeleton")
href_list["datumrefresh"] = href_list["make_skeleton"]
-
+
else if(href_list["offer_control"])
if(!check_rights(R_ADMIN)) return
@@ -751,7 +751,7 @@ client
switch(href_list["rotatedir"])
if("right") A.dir = turn(A.dir, -45)
if("left") A.dir = turn(A.dir, 45)
-
+
message_admins("[key_name_admin(usr)] has rotated \the [A]")
log_admin("[key_name(usr)] has rotated \the [A]")
href_list["datumrefresh"] = href_list["rotatedatum"]
@@ -1031,7 +1031,7 @@ client
usr << "This can only be done on mobs with clients"
return
- nanomanager.send_resources(H.client)
+ H.client.reload_nanoui_resources()
usr << "Resource files sent"
H << "Your NanoUI Resource files have been refreshed"
diff --git a/code/game/asteroid.dm b/code/game/asteroid.dm
index 48dfe916303..87e3ac87e67 100644
--- a/code/game/asteroid.dm
+++ b/code/game/asteroid.dm
@@ -88,8 +88,8 @@ var/global/max_secret_rooms = 6
theme = "cavein"
walltypes = list(/turf/simulated/mineral/random/high_chance=1)
floortypes = list(/turf/simulated/floor/plating/airless/asteroid, /turf/simulated/floor/beach/sand)
- treasureitems = list(/obj/mecha/working/ripley/mining=1, /obj/item/weapon/pickaxe/drill/diamonddrill=2,/obj/item/weapon/gun/energy/kinetic_accelerator=1,
- /obj/item/weapon/resonator=1, /obj/item/weapon/pickaxe/drill/jackhammer=5)
+ treasureitems = list(/obj/mecha/working/ripley/mining=1, /obj/item/weapon/pickaxe/drill/diamonddrill=2,/obj/item/weapon/gun/energy/kinetic_accelerator/hyper=1,
+ /obj/item/weapon/resonator/upgraded=1, /obj/item/weapon/pickaxe/drill/jackhammer=5)
fluffitems = list(/obj/effect/decal/cleanable/blood=3,/obj/effect/decal/remains/human=1,/obj/item/clothing/under/overalls=1,
/obj/item/weapon/reagent_containers/food/snacks/grown/chili=1,/obj/item/weapon/tank/oxygen/red=2)
@@ -165,6 +165,10 @@ var/global/max_secret_rooms = 6
valid = 0
continue
+ if(locate(/area/mine/abandoned) in surroundings)
+ valid = 0
+ continue
+
if(locate(/turf/space) in surroundings)
valid = 0
continue
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index 891be23fdf2..08aff0a1fad 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -451,7 +451,7 @@
if (src.occupant.client)
src.occupant.client.eye = src.occupant.client.mob
src.occupant.client.perspective = MOB_PERSPECTIVE
- src.occupant.loc = src.loc
+ src.occupant.forceMove(get_turf(src))
src.eject_wait = 0 //If it's still set somehow.
domutcheck(src.occupant) //Waiting until they're out before possible notransform.
src.occupant = null
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index 53a340696a4..e5b8956ad93 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -217,6 +217,15 @@
else
robot["cell"] = 0
+ var/turf/pos = get_turf(R)
+ var/area/bot_area = get_area(R)
+ robot["xpos"] = pos.x
+ robot["ypos"] = pos.y
+ robot["zpos"] = pos.z
+ robot["area"] = format_text(bot_area.name)
+
+ robot["health"] = round(R.health * 100 / R.maxHealth,0.1)
+
robot["module"] = R.module ? R.module.name : "None"
robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None"
robot["hackable"] = 0
diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index f4f2d4d4ec6..a7703f651ab 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -584,7 +584,7 @@ obj/item/weapon/circuitboard/rdserver
origin_tech = "programming=3;engineering=5;bluespace=5;materials=4"
frame_desc = "Requires 3 Bluespace Crystals and 1 Matter Bin."
req_components = list(
- /obj/item/bluespace_crystal = 3,
+ /obj/item/weapon/ore/bluespace_crystal = 3,
/obj/item/weapon/stock_parts/matter_bin = 1)
/obj/item/weapon/circuitboard/teleporter_station
@@ -594,7 +594,7 @@ obj/item/weapon/circuitboard/rdserver
origin_tech = "programming=4;engineering=4;bluespace=4"
frame_desc = "Requires 2 Bluespace Crystals, 2 Capacitors and 1 Console Screen."
req_components = list(
- /obj/item/bluespace_crystal = 2,
+ /obj/item/weapon/ore/bluespace_crystal = 2,
/obj/item/weapon/stock_parts/capacitor = 2,
/obj/item/weapon/stock_parts/console_screen = 1)
@@ -605,7 +605,7 @@ obj/item/weapon/circuitboard/rdserver
origin_tech = "programming=4;engineering=3;materials=3;bluespace=4"
frame_desc = "Requires 2 Bluespace Crystals, 1 Capacitor, 1 piece of cable and 1 Console Screen."
req_components = list(
- /obj/item/bluespace_crystal = 2,
+ /obj/item/weapon/ore/bluespace_crystal = 2,
/obj/item/weapon/stock_parts/capacitor = 1,
/obj/item/stack/cable_coil = 1,
/obj/item/weapon/stock_parts/console_screen = 1)
@@ -779,4 +779,15 @@ obj/item/weapon/circuitboard/rdserver
origin_tech = "programming=1;engineering=2"
req_components = list(
/obj/item/weapon/stock_parts/console_screen = 1,
- /obj/item/weapon/stock_parts/matter_bin = 3)
\ No newline at end of file
+ /obj/item/weapon/stock_parts/matter_bin = 3)
+
+/obj/item/weapon/circuitboard/clawgame
+ name = "circuit board (Claw Game)"
+ build_path = /obj/machinery/arcade/claw
+ board_type = "machine"
+ origin_tech = "programming=2"
+ req_components = list(
+ /obj/item/weapon/stock_parts.matter_bin = 1,
+ /obj/item/weapon/stock_parts/manipulator = 1,
+ /obj/item/stack/cable_coil = 5,
+ /obj/item/stack/sheet/glass = 1)
\ No newline at end of file
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index 7e132f64614..0c2894ff960 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -9,41 +9,48 @@ var/datum/announcement/minor/slotmachine_announcement = new(do_log = 0)
density = 1
var/plays = 0
var/working = 0
- var/balance=0
+ var/datum/money_account/account = null
+ var/result = null
+ var/resultlvl = null
-/obj/machinery/slot_machine/attack_hand(var/mob/user as mob)
- if(user.mind)
- if(user.mind.initial_account)
- balance = user.mind.initial_account.money
- user.machine = src
- if (working)
- var/dat = {"Slot Machine
-
- Please wait!
"}
- user << browse(dat, "window=slotmachine;size=450x500")
- onclose(user, "slotmachine")
- else
- var/dat = {"Slot Machine
-
- Five credits to play!
- Credits Remaining: [balance]
- [plays] players have tried their luck today!
-
- Play!
"}
- user << browse(dat, "window=slotmachine;size=400x500")
- onclose(user, "slotmachine")
+/obj/machinery/slot_machine/attack_hand(mob/user as mob)
+ account = user.get_worn_id_account()
+ if(!account)
+ if(istype(user.get_active_hand(), /obj/item/weapon/card/id))
+ account = get_card_account(user.get_active_hand())
+ else
+ account = null
+
+ ui_interact(user)
+
+/obj/machinery/slot_machine/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ var/data[0]
+
+ data["working"] = working
+ data["money"] = account ? account.money : null
+ data["plays"] = plays
+ data["result"] = result
+ data["resultlvl"] = resultlvl
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "slotmachine.tmpl", name, 350, 200)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
/obj/machinery/slot_machine/Topic(href, href_list)
+ add_fingerprint(usr)
+
if(href_list["ops"])
var/operation = text2num(href_list["ops"])
if(operation == 1) // Play
if (working == 1)
- usr << "You need to wait until the machine stops spinning!"
return
- if (balance < 5)
- usr << "Insufficient money to play!"
+ if (!account || account.money < 5)
+ return
+ if(!account.charge(5, transaction_purpose = "Bet", dest_name = name))
return
- usr.mind.initial_account.money -= 5
plays += 1
working = 1
icon_state = "slots-on"
@@ -51,26 +58,43 @@ var/datum/announcement/minor/slotmachine_announcement = new(do_log = 0)
playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
spawn(30)
if (roll == 1)
- playsound(src.loc, 'sound/effects/engine_alert2.ogg', 50, 0)
visible_message("[src] says, 'JACKPOT! [usr.name] has won two hundred and fifty thousand credits!'")
slotmachine_announcement.Announce("Congratulations to [usr.name] on winning the jackpot of two hundred and fifty thousand credits!", "Jackpot Winner")
- usr.mind.initial_account.money += 250000
+ result = "JACKPOT! You win two hundred and fifty thousand credits!"
+ resultlvl = "highlight"
+ win_money(250000, 'sound/effects/engine_alert2.ogg')
else if (roll > 1 && roll <= 10)
- playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
visible_message("[src] says, 'Big Winner! [usr.name] has won five thousand credits!'")
- usr.mind.initial_account.money += 5000
+ result = "Big Winner! You win five thousand credits!"
+ resultlvl = "good"
+ win_money(5000)
else if (roll > 10 && roll <= 100)
- playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
- visible_message("[src] says, 'Winner! [usr.name] has won win five hundred credits!'")
- usr.mind.initial_account.money += 500
+ visible_message("[src] says, 'Winner! [usr.name] has won five hundred credits!'")
+ result = "You win five hundred credits!"
+ resultlvl = "good"
+ win_money(500)
else if (roll > 100 && roll <= 1000)
- playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
- usr << "You win 5 credits!"
- usr.mind.initial_account.money += 5
+ result = "You win 5 credits!"
+ resultlvl = "good"
+ win_money(5)
else
- usr << "No luck!"
+ result = "No luck!"
+ resultlvl = "average"
working = 0
icon_state = "slots-off"
- add_fingerprint(usr)
- updateUsrDialog()
- return
+
+/obj/machinery/slot_machine/proc/win_money(amt, sound='sound/machines/ping.ogg')
+ if(sound)
+ playsound(loc, sound, 50)
+
+ if(!account)
+ return
+ account.money += amt
+
+ var/datum/transaction/T = new()
+ T.target_name = account.owner_name
+ T.purpose = "Slot Winnings"
+ T.amount = "[amt]"
+ T.date = current_date_string
+ T.time = worldtime2text()
+ account.transaction_log.Add(T)
\ No newline at end of file
diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm
index 2df044d7e8a..6a4dbe224d7 100644
--- a/code/game/machinery/teleporter.dm
+++ b/code/game/machinery/teleporter.dm
@@ -276,9 +276,9 @@
link_power_station()
component_parts = list()
component_parts += new /obj/item/weapon/circuitboard/teleporter_hub(null)
- component_parts += new /obj/item/bluespace_crystal/artificial(null)
- component_parts += new /obj/item/bluespace_crystal/artificial(null)
- component_parts += new /obj/item/bluespace_crystal/artificial(null)
+ component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null)
+ component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null)
+ component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null)
component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
RefreshParts()
@@ -286,9 +286,9 @@
..()
component_parts = list()
component_parts += new /obj/item/weapon/circuitboard/teleporter_hub(null)
- component_parts += new /obj/item/bluespace_crystal/artificial(null)
- component_parts += new /obj/item/bluespace_crystal/artificial(null)
- component_parts += new /obj/item/bluespace_crystal/artificial(null)
+ component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null)
+ component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null)
+ component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null)
component_parts += new /obj/item/weapon/stock_parts/matter_bin/super(null)
RefreshParts()
@@ -385,8 +385,8 @@
..()
component_parts = list()
component_parts += new /obj/item/weapon/circuitboard/teleporter_station(null)
- component_parts += new /obj/item/bluespace_crystal/artificial(null)
- component_parts += new /obj/item/bluespace_crystal/artificial(null)
+ component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null)
+ component_parts += new /obj/item/weapon/ore/bluespace_crystal/artificial(null)
component_parts += new /obj/item/weapon/stock_parts/capacitor(null)
component_parts += new /obj/item/weapon/stock_parts/capacitor(null)
component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index 8d414c2168a..49dd158adf3 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -1096,7 +1096,7 @@
icon_state = "engivend"
icon_deny = "engivend-deny"
req_access_txt = "11" //Engineering Equipment access
- products = list(/obj/item/clothing/glasses/meson = 2,/obj/item/device/multitool = 4,/obj/item/weapon/airlock_electronics = 10,/obj/item/weapon/apc_electronics = 10,/obj/item/weapon/airalarm_electronics = 10,/obj/item/weapon/stock_parts/cell/high = 10)
+ products = list(/obj/item/clothing/glasses/meson = 2,/obj/item/device/multitool = 4,/obj/item/weapon/airlock_electronics = 10,/obj/item/weapon/firealarm_electronics = 10,/obj/item/weapon/apc_electronics = 10,/obj/item/weapon/airalarm_electronics = 10,/obj/item/weapon/stock_parts/cell/high = 10)
contraband = list(/obj/item/weapon/stock_parts/cell/potato = 3)
premium = list(/obj/item/weapon/storage/belt/utility = 3)
diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm
index 12a64dc01bb..41b6b99fb87 100644
--- a/code/game/mecha/equipment/tools/tools.dm
+++ b/code/game/mecha/equipment/tools/tools.dm
@@ -1090,7 +1090,9 @@
return
var/list/occupant = list()
occupant |= mecha.occupant
+ mecha.occupant.sight |= SEE_TURFS
scanning = 1
mineral_scan_pulse(occupant,get_turf(loc))
spawn(equip_cooldown)
scanning = 0
+ mecha.occupant.sight -= SEE_TURFS
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index a7cdb7a0049..9e335de1f07 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -8,7 +8,6 @@
use_power = 1
idle_power_usage = 20
active_power_usage = 5000
- req_access = list(access_robotics)
var/time_coeff = 1
var/resource_coeff = 1
var/time_coeff_tech = 1
@@ -92,36 +91,6 @@
time_coeff = round(initial(time_coeff) - (initial(time_coeff)*(T))/5,0.01)
-/obj/machinery/mecha_part_fabricator/check_access(obj/item/weapon/card/id/I)
- if(istype(I, /obj/item/device/pda))
- var/obj/item/device/pda/pda = I
- I = pda.id
- if(!istype(I) || !I.access) //not ID or no access
- return 0
- for(var/req in req_access)
- if(!(req in I.access)) //doesn't have this access
- return 0
- return 1
-
-/obj/machinery/mecha_part_fabricator/proc/emag()
- switch(emagged)
- if(0)
- emagged = 0.5
- visible_message("\icon[src] \The [src] beeps: \"DB error \[Code 0x00F1\]\"")
- sleep(10)
- visible_message("\icon[src] \The [src] beeps: \"Attempting auto-repair\"")
- sleep(15)
- visible_message("\icon[src] \The [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"")
- sleep(30)
- visible_message("\icon[src] \The [src] beeps: \"User DB truncated. Please contact your Nanotrasen system operator for future assistance.\"")
- req_access = null
- emagged = 1
- if(0.5)
- visible_message("\icon[src] \The [src] beeps: \"DB not responding \[Code 0x0003\]...\"")
- if(1)
- visible_message("\icon[src] \The [src] beeps: \"No records in User DB\"")
- return
-
/obj/machinery/mecha_part_fabricator/proc/output_parts_list(set_name)
var/output = ""
for(var/datum/design/D in files.known_designs)
@@ -149,9 +118,9 @@
var/output
for(var/resource in resources)
var/amount = min(res_max_amount, resources[resource])
- output += "[material2name(resource)]: [amount] cm³"
+ output += "[material2name(resource)]: [amount] cm³, [round(resources[resource] / MINERAL_MATERIAL_AMOUNT,0.1)] sheets"
if(amount>0)
- output += " - Remove \[1\] | \[10\] | \[All\]"
+ output += " - Remove \[1\] | \[10\] | \[Custom\] | \[All\]"
output += "
"
return output
@@ -319,8 +288,12 @@
interact(user)
/obj/machinery/mecha_part_fabricator/attack_hand(mob/user)
- if(!(..()))
- return interact(user)
+ if(..())
+ return 1
+ if(!allowed(user) && !isobserver(user))
+ user << "Access denied."
+ return 1
+ return interact(user)
/obj/machinery/mecha_part_fabricator/interact(mob/user as mob)
var/dat, left_part
@@ -455,7 +428,13 @@
if(href_list["remove_mat"] && href_list["material"])
var/amount = text2num(href_list["remove_mat"])
var/material = href_list["material"]
- if(amount < 0 || amount > resources[material]) //href protection
+ if(href_list["custom_eject"])
+ amount = input("How many sheets would you like to eject from the machine?", "How much?", 1) as null|num
+ amount = max(0,min(round(resources[material]/MINERAL_MATERIAL_AMOUNT),amount)) // Rounding errors aren't scary, as the mineral eject proc is smart
+ if (!amount)
+ return
+ amount = round(amount)
+ if(amount < 0 || amount > resources[material]) //href protection, except that resources[] is 2000 per sheet
return
var/removed = remove_material(material,amount)
@@ -569,8 +548,5 @@
user << "\The [src] cannot hold any more [sname] sheet\s."
return
-/obj/machinery/mecha_part_fabricator/emag_act(user as mob)
- emag()
-
/obj/machinery/mecha_part_fabricator/proc/material2name(var/ID)
return copytext(ID,2)
\ No newline at end of file
diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm
index 5e684b9ddef..844ccd978eb 100644
--- a/code/game/mecha/mecha_construction_paths.dm
+++ b/code/game/mecha/mecha_construction_paths.dm
@@ -1204,7 +1204,7 @@
"backkey"=/obj/item/weapon/crowbar,
"desc"="The bluespace crystal is installed."),
//10
- list("key"=/obj/item/bluespace_crystal,
+ list("key"=/obj/item/weapon/ore/bluespace_crystal,
"backkey"=/obj/item/weapon/screwdriver,
"desc"="Super capacitor is secured."),
//11
@@ -1394,7 +1394,7 @@
holder.icon_state = "phazon16"
else
user.visible_message("[user] removes the bluespace crystal from the [holder].", "You remove the bluespace crystal from the [holder].")
- new /obj/item/bluespace_crystal(get_turf(holder))
+ new /obj/item/weapon/ore/bluespace_crystal(get_turf(holder))
holder.icon_state = "phazon14"
if(8)
if(diff==FORWARD)
diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm
index 3eba9cc2132..0666f6472af 100644
--- a/code/game/mecha/working/ripley.dm
+++ b/code/game/mecha/working/ripley.dm
@@ -20,6 +20,10 @@
return
*/
+/obj/mecha/working/ripley/Move()
+ . = ..()
+ update_pressure()
+
/obj/mecha/working/ripley/Destroy()
while(src.damage_absorption.["brute"] < 0.6)
new /obj/item/asteroid/goliath_hide(src.loc)
@@ -165,4 +169,18 @@
if(T)
T.Entered(A)
step_rand(A)
- return ..()
\ No newline at end of file
+ return ..()
+
+/obj/mecha/working/ripley/proc/update_pressure()
+ var/turf/T = get_turf(loc)
+ var/datum/gas_mixture/environment = T.return_air()
+ var/pressure = environment.return_pressure()
+
+ if(pressure < 20)
+ step_in = 3
+ for(var/obj/item/mecha_parts/mecha_equipment/tool/drill/drill in equipment)
+ drill.equip_cooldown = initial(drill.equip_cooldown)/2
+ else
+ step_in = 5
+ for(var/obj/item/mecha_parts/mecha_equipment/tool/drill/drill in equipment)
+ drill.equip_cooldown = initial(drill.equip_cooldown)
\ No newline at end of file
diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm
index 39196db70fa..d39a8dfbc93 100644
--- a/code/game/objects/items/devices/flash.dm
+++ b/code/game/objects/items/devices/flash.dm
@@ -111,11 +111,13 @@
else if(issilicon(M))
if(isrobot(M))
var/mob/living/silicon/robot/R = M
- for(var/obj/item/borg/combat/shield/S in R.module.modules)
- if(R.activated(S))
- add_logs(M, user, "flashed", object="[src.name]")
- user.visible_message("[user] tries to overloads [M]'s sensors with the [src.name], but if blocked by [M]'s shield!", "You try to overload [M]'s sensors with the [src.name], but are blocked by his shield!")
- return 1
+
+ if (R.module) // Perhaps they didn't choose a module yet
+ for(var/obj/item/borg/combat/shield/S in R.module.modules)
+ if(R.activated(S))
+ add_logs(M, user, "flashed", object="[src.name]")
+ user.visible_message("[user] tries to overloads [M]'s sensors with the [src.name], but if blocked by [M]'s shield!", "You try to overload [M]'s sensors with the [src.name], but are blocked by his shield!")
+ return 1
M.Weaken(rand(5,10))
add_logs(M, user, "flashed", object="[src.name]")
user.visible_message("[user] overloads [M]'s sensors with the [src.name]!", "You overload [M]'s sensors with the [src.name]!")
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index c99415ea5b5..425b7e0b54f 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -217,7 +217,11 @@
return 1
var/to_transfer as num
if (user.get_inactive_hand()==src)
- to_transfer = 1
+ var/desired = input("How much would you like to transfer from this stack?", "How much?", 1) as null|num
+ if(!desired)
+ return
+ desired = round(desired)
+ to_transfer = max(1,min(desired,S.max_amount-S.amount,src.amount))
else
to_transfer = min(src.amount, S.max_amount-S.amount)
S.amount+=to_transfer
diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm
index ee203dad9b4..2118e493687 100644
--- a/code/game/objects/items/weapons/cards_ids.dm
+++ b/code/game/objects/items/weapons/cards_ids.dm
@@ -122,9 +122,8 @@
user << "It is too far away."
/obj/item/weapon/card/id/proc/show(mob/user as mob)
- if(user.client) // Send the stamp images to the client
- var/datum/asset/simple/S = new/datum/asset/simple/paper()
- send_asset_list(user.client, S.assets)
+ var/datum/asset/assets = get_asset_datum(/datum/asset/simple/paper)
+ assets.send(user)
if(!front)
front = new(photo, dir = SOUTH)
diff --git a/code/game/objects/structures/crates_lockers/walllocker.dm b/code/game/objects/structures/crates_lockers/walllocker.dm
index 2937e7b58ac..03a81a7ca5b 100644
--- a/code/game/objects/structures/crates_lockers/walllocker.dm
+++ b/code/game/objects/structures/crates_lockers/walllocker.dm
@@ -11,6 +11,10 @@
icon_closed = "wall-locker"
icon_opened = "wall-lockeropen"
+/obj/structure/closet/walllocker/close()
+ ..()
+ density = 0 //It's a locker in a wall, you aren't going to be walking into it.
+
//spawns endless (3 sets) amounts of breathmask, emergency oxy tank and crowbar
/obj/structure/closet/walllocker/emerglocker
@@ -19,12 +23,14 @@
var/list/spawnitems = list(/obj/item/weapon/tank/emergency_oxygen,/obj/item/clothing/mask/breath,/obj/item/weapon/crowbar)
var/amount = 3 // spawns each items X times.
icon_state = "emerg"
+ icon_closed = "emerg"
+ icon_opened = "emergopen"
/obj/structure/closet/walllocker/emerglocker/attack_hand(mob/user as mob)
if (istype(user, /mob/living/silicon/ai)) //Added by Strumpetplaya - AI shouldn't be able to
return //activate emergency lockers. This fixes that. (Does this make sense, the AI can't call attack_hand, can it? --Mloc)
if(!amount)
- usr << "It's empty.."
+ usr << "It's empty."
return
if(amount)
usr << "You take out some items from \the [src]."
diff --git a/code/game/vehicles/spacepods/pod_fabricator.dm b/code/game/vehicles/spacepods/pod_fabricator.dm
index fcc65870b1e..a3d9e4911a3 100644
--- a/code/game/vehicles/spacepods/pod_fabricator.dm
+++ b/code/game/vehicles/spacepods/pod_fabricator.dm
@@ -8,7 +8,6 @@
use_power = 1
idle_power_usage = 20
active_power_usage = 5000
- req_access = list(access_robotics)
var/time_coeff = 1
var/resource_coeff = 1
var/time_coeff_tech = 1
@@ -97,37 +96,6 @@
T += Ml.rating
time_coeff = round(initial(time_coeff) - (initial(time_coeff)*(T))/5,0.01)
-
-/obj/machinery/spod_part_fabricator/check_access(obj/item/weapon/card/id/I)
- if(istype(I, /obj/item/device/pda))
- var/obj/item/device/pda/pda = I
- I = pda.id
- if(!istype(I) || !I.access) //not ID or no access
- return 0
- for(var/req in req_access)
- if(!(req in I.access)) //doesn't have this access
- return 0
- return 1
-
-/obj/machinery/spod_part_fabricator/proc/emag()
- switch(emagged)
- if(0)
- emagged = 0.5
- visible_message("\icon[src] \The [src] beeps: \"DB error \[Code 0x00F1\]\"")
- sleep(10)
- visible_message("\icon[src] \The [src] beeps: \"Attempting auto-repair\"")
- sleep(15)
- visible_message("\icon[src] \The [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"")
- sleep(30)
- visible_message("\icon[src] \The [src] beeps: \"User DB truncated. Please contact your Nanotrasen system operator for future assistance.\"")
- req_access = null
- emagged = 1
- if(0.5)
- visible_message("\icon[src] \The [src] beeps: \"DB not responding \[Code 0x0003\]...\"")
- if(1)
- visible_message("\icon[src] \The [src] beeps: \"No records in User DB\"")
- return
-
/obj/machinery/spod_part_fabricator/proc/output_parts_list(set_name)
var/output = ""
for(var/datum/design/D in files.known_designs)
@@ -155,9 +123,9 @@
var/output
for(var/resource in resources)
var/amount = min(res_max_amount, resources[resource])
- output += "[material2name(resource)]: [amount] cm³"
+ output += "[material2name(resource)]: [amount] cm³, [round(resources[resource] / MINERAL_MATERIAL_AMOUNT,0.1)] sheets"
if(amount>0)
- output += " - Remove \[1\] | \[10\] | \[All\]"
+ output += " - Remove \[1\] | \[10\] | \[Custom\] | \[All\]"
output += "
"
return output
@@ -322,8 +290,9 @@
return round(initial(D.construction_time)*time_coeff*time_coeff_tech, roundto)
/obj/machinery/spod_part_fabricator/attack_hand(mob/user)
- if(!(..()))
- return interact(user)
+ if(..())
+ return 1
+ return interact(user)
/obj/machinery/spod_part_fabricator/interact(mob/user as mob)
var/dat, left_part
@@ -457,6 +426,12 @@
if(href_list["remove_mat"] && href_list["material"])
var/amount = text2num(href_list["remove_mat"])
var/material = href_list["material"]
+ if(href_list["custom_eject"])
+ amount = input("How many sheets would you like to eject from the machine?", "How much?", 1) as null|num
+ amount = max(0,min(round(resources[material]/MINERAL_MATERIAL_AMOUNT),amount)) // Rounding errors aren't scary, as the mineral eject proc is smart
+ if (!amount)
+ return
+ amount = round(amount)
if(amount < 0 || amount > resources[material]) //href protection
return
@@ -571,8 +546,5 @@
user << "\The [src] cannot hold any more [sname] sheet\s."
return
-/obj/machinery/spod_part_fabricator/emag_act(user as mob)
- emag()
-
/obj/machinery/spod_part_fabricator/proc/material2name(var/ID)
return copytext(ID,2)
\ No newline at end of file
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index 37c6a60bcdd..7565d710091 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -3,6 +3,10 @@ world/IsBanned(key,address,computer_id)
if (!key || !address || !computer_id)
log_access("Failed Login (invalid data): [key] [address]-[computer_id]")
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, please try again. Error message: Your computer provided invalid or blank information to the server on connection (BYOND Username, IP, and Computer ID). Provided information for reference: Username: '[key]' IP: '[address]' Computer ID: '[computer_id]'. If you continue to get this error, please restart byond or contact byond support.")
+
+ if (computer_id == 2147483647) //this cid causes stickybans to go haywire
+ log_access("Failed Login (invalid cid): [key] [address]-[computer_id]")
+ return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided an invalid Computer ID.")
var/admin = 0
var/ckey = ckey(key)
if((ckey in admin_datums) || (ckey in deadmins))
@@ -20,7 +24,7 @@ world/IsBanned(key,address,computer_id)
message_admins("Failed Login: [key] - Banned: Tor")
//ban their computer_id and ckey for posterity
AddBan(ckey(key), computer_id, "Use of Tor", "Automated Ban", 0, 0)
- var/mistakemessage = ""
+ var/mistakemessage = ""
if(config.banappeals)
mistakemessage = "\nIf you believe this is a mistake, please request help at [config.banappeals]."
return list("reason"="using Tor", "desc"="\nReason: The network you are using to connect has been banned.[mistakemessage]")
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index ae97f0e6ad2..d595c46abd3 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -1177,3 +1177,18 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
log_admin("[key_name(src)] has toggled [M.key]'s [blockname] block [state]!")
else
alert("Invalid mob")
+
+/client/proc/reload_nanoui_resources()
+ set category = "Debug"
+ set name = "Reload NanoUI Resources"
+ set desc = "Force the client to redownload NanoUI Resources"
+
+ // Close open NanoUIs.
+ nanomanager.close_user_uis(usr)
+
+ // Re-load the assets.
+ var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui)
+ assets.register()
+
+ // Clear the user's cache so they get resent.
+ usr.client.cache = list()
\ No newline at end of file
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index e46d72c338e..0af366ef215 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -138,7 +138,8 @@ var/list/admin_verbs_show_debug_verbs = list(
/client/proc/print_jobban_old,
/client/proc/print_jobban_old_filter,
/client/proc/forceEvent,
- /client/proc/nanomapgen_DumpImage
+ /client/proc/nanomapgen_DumpImage,
+ /client/proc/reload_nanoui_resources
)
/client/proc/enable_debug_verbs()
diff --git a/code/modules/arcade/arcade_base.dm b/code/modules/arcade/arcade_base.dm
new file mode 100644
index 00000000000..da83338b5f3
--- /dev/null
+++ b/code/modules/arcade/arcade_base.dm
@@ -0,0 +1,165 @@
+
+/obj/machinery/arcade
+ name = "Arcade Game"
+ desc = "One of the most generic arcade games ever."
+ icon = 'icons/obj/arcade.dmi'
+ icon_state = "clawmachine_on"
+ density = 1
+ anchored = 1
+ use_power = 1
+ idle_power_usage = 40
+ var/tokens = 0
+ var/freeplay = 0 //for debugging and admin kindness
+ var/token_price = 0
+ var/last_winner = null //for letting people who to hunt down and steal prizes from
+ var/window_name = "arcade" //in case you want to change the window name for certain machines
+
+/obj/machinery/arcade/New()
+ ..()
+ var/choice = pick(subtypesof(/obj/machinery/arcade))
+ new choice(loc)
+ qdel(src)
+
+/obj/machinery/arcade/examine(mob/user)
+ ..(user)
+ if(freeplay)
+ user << "Someone enabled freeplay on this machine!"
+ else
+ if(token_price)
+ user << "\The [src.name] costs [token_price] credits per play."
+ if(!tokens)
+ user << "\The [src.name] has no available play credits. Better feed the machine!"
+ else if(tokens == 1)
+ user << "\The [src.name] has only 1 play credit left!"
+ else
+ user << "\The [src.name] has [tokens] play credits!"
+
+/obj/machinery/arcade/attack_hand(mob/user as mob)
+ if(..())
+ if(in_use && src == user.machine) //this one checks if they fell down/died and closes the game
+ src.close_game()
+ return
+ if(in_use && src == user.machine) //this one just checks if they are playing so it doesn't eat tokens
+ return
+ interact(user)
+
+/obj/machinery/arcade/interact(mob/user as mob)
+ if(stat & BROKEN || panel_open)
+ return
+ if(!tokens && !freeplay)
+ user << "\The [src.name] doesn't have enough credits to play! Pay first!"
+ return
+ if(!in_use && (tokens || freeplay))
+ in_use = 1
+ start_play(user)
+ return
+ if(in_use)
+ if(src != user.machine)
+ user << "Someone else is already playing this machine, please wait your turn!"
+ return
+
+/obj/machinery/arcade/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
+ if(istype(O, /obj/item/weapon/screwdriver) && anchored)
+ playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
+ panel_open = !panel_open
+ user << "You [panel_open ? "open" : "close"] the maintenance panel."
+ update_icon()
+ return
+ if(!freeplay)
+ if(istype(O, /obj/item/weapon/card/id))
+ var/obj/item/weapon/card/id/C = O
+ if(pay_with_card(C))
+ tokens += 1
+ return
+ else if(istype(O, /obj/item/weapon/spacecash))
+ var/obj/item/weapon/spacecash/C = O
+ if(pay_with_cash(C, user))
+ tokens += 1
+ return
+ if(panel_open&& component_parts && istype(O, /obj/item/weapon/crowbar))
+ default_deconstruction_crowbar(O)
+
+/obj/machinery/arcade/update_icon()
+ return
+
+/obj/machinery/arcade/proc/pay_with_cash(var/obj/item/weapon/spacecash/cashmoney, var/mob/user)
+ if(cashmoney.get_total() < token_price)
+ user << "\icon[cashmoney] That is not enough money."
+ return 0
+ visible_message("[usr] inserts a credit chip into [src].")
+ var/left = cashmoney.get_total() - token_price
+ user.unEquip(cashmoney)
+ qdel(cashmoney)
+ if(left)
+ dispense_cash(left, src.loc, user)
+ return 1
+
+/obj/machinery/arcade/proc/pay_with_card(var/obj/item/weapon/card/id/I, var/mob/user)
+ visible_message("[usr] swipes a card through [src].")
+ var/datum/money_account/customer_account = attempt_account_access_nosec(I.associated_account_number)
+ if (!customer_account)
+ user <<"Error: Unable to access account. Please contact technical support if problem persists."
+ return 0
+
+ if(customer_account.suspended)
+ user << "Unable to access account: account suspended."
+ return 0
+
+ // Have the customer punch in the PIN before checking if there's enough money. Prevents people from figuring out acct is
+ // empty at high security levels
+ if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2)
+ var/attempt_pin = input("Enter pin code", "Vendor transaction") as num
+ customer_account = attempt_account_access(I.associated_account_number, attempt_pin, 2)
+
+ if(!customer_account)
+ user << "Unable to access account: incorrect credentials."
+ return 0
+
+ if(token_price > customer_account.money)
+ user << "Insufficient funds in account."
+ return 0
+ else
+ // Okay to move the money at this point
+
+ // debit money from the purchaser's account
+ customer_account.money -= token_price
+
+ // create entry in the purchaser's account log
+ var/datum/transaction/T = new()
+ T.target_name = "[src.name]"
+ T.purpose = "Purchase of [src.name] credit"
+ if(token_price > 0)
+ T.amount = "([token_price])"
+ else
+ T.amount = "[token_price]"
+ T.source_terminal = src.name
+ T.date = current_date_string
+ T.time = worldtime2text()
+ customer_account.transaction_log.Add(T)
+ return 1
+
+/obj/machinery/arcade/proc/start_play(mob/user as mob)
+ user.set_machine(src)
+ if(!freeplay)
+ tokens -= 1
+
+/obj/machinery/arcade/proc/close_game()
+ in_use = 0
+ for(var/mob/user in viewers(world.view, src)) // I don't know who you are.
+ if(user.client && user.machine == src) // I will look for you,
+ user.unset_machine() // I will find you,
+ user << browse(null, "window=[window_name]") // And I will kill you.
+ return
+
+/obj/machinery/arcade/proc/win()
+ return
+
+/obj/machinery/arcade/process()
+ if(in_use)
+ src.updateUsrDialog()
+ if(!in_use)
+ src.close_game()
+
+/obj/machinery/arcade/Destroy()
+ src.close_game()
+ return ..()
\ No newline at end of file
diff --git a/code/modules/arcade/arcade_prize.dm b/code/modules/arcade/arcade_prize.dm
new file mode 100644
index 00000000000..30374d31a52
--- /dev/null
+++ b/code/modules/arcade/arcade_prize.dm
@@ -0,0 +1,24 @@
+
+/obj/item/toy/prizeball
+ name = "prize ball"
+ desc = "A toy is a toy, but a prize ball could be anything! It could even be a toy!"
+ icon = 'icons/obj/arcade.dmi'
+ icon_state = "prizeball_1"
+ var/opening = 0
+
+/obj/item/toy/prizeball/New()
+ ..()
+ icon_state = pick("prizeball_1","prizeball_2","prizeball_3")
+
+/obj/item/toy/prizeball/attack_self(mob/user as mob)
+ if(opening)
+ return
+ opening = 1
+ playsound(src.loc, 'sound/items/bubblewrap.ogg', 30, 1, extrarange = -4, falloff = 10)
+ icon_state = "prizeconfetti"
+ src.color = pick(random_color_list)
+ var/prize_inside = pick(/obj/random/carp_plushie, /obj/random/plushie, /obj/random/figure) //will add ticket bundles later
+ spawn(10)
+ user.unEquip(src)
+ new prize_inside(user.loc)
+ qdel(src)
\ No newline at end of file
diff --git a/code/modules/arcade/claw_game.dm b/code/modules/arcade/claw_game.dm
new file mode 100644
index 00000000000..9d423383b08
--- /dev/null
+++ b/code/modules/arcade/claw_game.dm
@@ -0,0 +1,82 @@
+/var/claw_game_html = null
+
+/obj/machinery/arcade/claw
+ name = "Claw Game"
+ desc = "One of the most infuriating ways to win a toy."
+ icon = 'icons/obj/arcade.dmi'
+ icon_state = "clawmachine_1_on"
+ token_price = 15
+ window_name = "Claw Game"
+ var/machine_image = "_1"
+ var/bonus_prize_chance = 5 //chance to dispense a SECOND prize if you win, increased by matter bin rating
+
+ //This is to make sure the images are available
+ var/list/img_resources = list('icons/obj/arcade_images/backgroundsprite.png',
+ 'icons/obj/arcade_images/clawpieces.png',
+ 'icons/obj/arcade_images/crane_bot.png',
+ 'icons/obj/arcade_images/crane_top.png',
+ 'icons/obj/arcade_images/prize_inside.png',
+ 'icons/obj/arcade_images/prizeorbs.png')
+
+/obj/machinery/arcade/claw/New()
+ src.addAtProcessing()
+
+ machine_image = pick("_1", "_2")
+ update_icon()
+
+ component_parts = list()
+ component_parts += new /obj/item/weapon/circuitboard/clawgame(null)
+ component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
+ component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
+ component_parts += new /obj/item/stack/cable_coil(null, 5)
+ component_parts += new /obj/item/stack/sheet/glass(null, 1)
+ RefreshParts()
+
+ if(!claw_game_html)
+ claw_game_html = file2text('code/modules/arcade/crane.html')
+
+/obj/machinery/arcade/claw/RefreshParts()
+ var/bin_upgrades = 0
+ for(var/obj/item/weapon/stock_parts/matter_bin/B in component_parts)
+ bin_upgrades = B.rating
+ bonus_prize_chance = bin_upgrades * 5 //equals +5% chance per matter bin rating level (+20% with rating 4)
+
+/obj/machinery/arcade/claw/update_icon()
+ if(stat & BROKEN)
+ icon_state = "clawmachine[machine_image]_broken"
+ else if(panel_open)
+ icon_state = "clawmachine[machine_image]_open"
+ else if(stat & NOPOWER)
+ icon_state = "clawmachine[machine_image]_off"
+ else
+ icon_state = "clawmachine[machine_image]_on"
+ return
+
+/obj/machinery/arcade/claw/win()
+ icon_state = "clawmachine[machine_image]_win"
+ visible_message("[src.name] beeps, \"WINNER!\"")
+ if(prob(bonus_prize_chance))
+ //double prize mania!
+ new /obj/item/toy/prizeball(get_turf(src))
+ new /obj/item/toy/prizeball(get_turf(src))
+ playsound(src.loc, 'sound/arcade/Win.ogg', 50, 1, extrarange = -3, falloff = 10)
+ spawn(10)
+ update_icon()
+
+/obj/machinery/arcade/claw/start_play(mob/user as mob)
+ ..()
+ user << browse_rsc('page.css')
+ for(var/i=1, i<=img_resources.len, i++)
+ user << browse_rsc(img_resources[i])
+ var/my_game_html = replacetext(claw_game_html, "/* ref src */", "\ref[src]")
+ user << browse(my_game_html, "window=[window_name];size=700x600")
+
+/obj/machinery/arcade/claw/Topic(href, list/href_list)
+ if(..())
+ return
+ var/prize_won = null
+ prize_won = href_list["prizeWon"]
+ if(!isnull(prize_won))
+ close_game()
+ if(prize_won == "1")
+ win()
\ No newline at end of file
diff --git a/code/modules/arcade/crane.html b/code/modules/arcade/crane.html
new file mode 100644
index 00000000000..3cf0fc67ae3
--- /dev/null
+++ b/code/modules/arcade/crane.html
@@ -0,0 +1,289 @@
+
+
+
+
+ Crane game
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/modules/arcade/page.css b/code/modules/arcade/page.css
new file mode 100644
index 00000000000..1c569967750
--- /dev/null
+++ b/code/modules/arcade/page.css
@@ -0,0 +1,84 @@
+#background {
+ width:900px;
+ height:406px;
+ position: absolute;
+ background: url(backgroundsprite.png) no-repeat;
+ background-position: 0 -166px;
+ top: 62px;
+}
+
+#grayorbs-chute {
+ width: 902px;
+ height: 131px;
+ position: absolute;
+ background: url(backgroundsprite.png) no-repeat center;
+ background-position: 3px -15px;
+ top: 337px;
+ left: -3px;
+}
+
+#foreground {
+ width:900px;
+ height:520px;
+ position: absolute;
+ background: url(backgroundsprite.png) no-repeat center;
+ background-position: 0 -575px;
+ z-index:10;
+}
+
+#crane {
+ width:100px;
+ height:100px;
+ position: absolute;
+ top: 131px;
+ left: 100px;
+}
+
+#crane-handle-top {
+ width: 10px;
+ height: 83px;
+ position: absolute;
+ background: url('crane_top.png') repeat-y center;
+ bottom: 86px;/*top: -69px;*/
+ left: 47px;
+}
+
+#crane-handle-bottom {
+ width:10px;
+ height:8px;
+ position: absolute;
+ background: url('clawpieces.png') no-repeat center;
+ top: 14px;
+ left: 47px;
+ background-position: -198px -83px;
+}
+
+#crane-center{
+ width: 26px;
+ height: 27px;
+ position: absolute;
+ background: url('clawpieces.png') no-repeat center;
+ top: 22px;
+ left: 39px;
+ background-position: -190px -101px;
+}
+
+#crane-claw {
+ width: 110px;
+ height: 78px;
+ position: absolute;
+ background: url('clawpieces.png') no-repeat center;
+ top: 33px;
+ left: 11px;
+ background-position: -36px -34px;
+}
+
+.prize-ball {
+ width: 60px;
+ height: 60px;
+ //background: url('prizeorbs.png') no-repeat;
+ //-moz-border-radius: 30px;
+ //-webkit-border-radius: 30px;
+ //border-radius: 30px;
+ position: absolute;
+}
\ No newline at end of file
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
new file mode 100644
index 00000000000..d52f9947193
--- /dev/null
+++ b/code/modules/client/asset_cache.dm
@@ -0,0 +1,241 @@
+/*
+Asset cache quick users guide:
+
+Make a datum at the bottom of this file with your assets for your thing.
+The simple subsystem will most like be of use for most cases.
+Then call get_asset_datum() with the type of the datum you created and store the return
+Then call .send(client) on that stored return value.
+
+You can set verify to TRUE if you want send() to sleep until the client has the assets.
+*/
+
+
+// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping.
+// This is doubled for the first asset, then added per asset after
+#define ASSET_CACHE_SEND_TIMEOUT 7
+
+//When sending mutiple assets, how many before we give the client a quaint little sending resources message
+#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
+
+//List of ALL assets for the above, format is list(filename = asset).
+/var/global/list/asset_cache = list()
+
+/client
+ var/list/cache = list() // List of all assets sent to this client by the asset cache.
+ var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement.
+ var/list/sending = list()
+ var/last_asset_job = 0 // Last job done.
+
+//This proc sends the asset to the client, but only if it needs it.
+//This proc blocks(sleeps) unless verify is set to false
+/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE)
+ if(!istype(client))
+ if(ismob(client))
+ var/mob/M = client
+ if(M.client)
+ client = M.client
+
+ else
+ return 0
+
+ else
+ return 0
+
+ if(client.cache.Find(asset_name) || client.sending.Find(asset_name))
+ return 0
+
+ client << browse_rsc(asset_cache[asset_name], asset_name)
+ if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
+ if (client)
+ client.cache += asset_name
+ return 1
+ if (!client)
+ return 0
+
+ client.sending |= asset_name
+ var/job = ++client.last_asset_job
+
+ client << browse({"
+
+ "}, "window=asset_cache_browser")
+
+ var/t = 0
+ var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT
+ while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
+ sleep(1) // Lock up the caller until this is received.
+ t++
+
+ if(client)
+ client.sending -= asset_name
+ client.cache |= asset_name
+ client.completed_asset_jobs -= job
+
+ return 1
+
+//This proc blocks(sleeps) unless verify is set to false
+/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE)
+ if(!istype(client))
+ if(ismob(client))
+ var/mob/M = client
+ if(M.client)
+ client = M.client
+
+ else
+ return 0
+
+ else
+ return 0
+
+ var/list/unreceived = asset_list - (client.cache + client.sending)
+ if(!unreceived || !unreceived.len)
+ return 0
+ if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
+ client << "Sending Resources..."
+ for(var/asset in unreceived)
+ if (asset in asset_cache)
+ client << browse_rsc(asset_cache[asset], asset)
+
+ if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
+ if (client)
+ client.cache += unreceived
+ return 1
+ if (!client)
+ return 0
+ client.sending |= unreceived
+ var/job = ++client.last_asset_job
+
+ client << browse({"
+
+ "}, "window=asset_cache_browser")
+
+ var/t = 0
+ var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len
+ while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
+ sleep(1) // Lock up the caller until this is received.
+ t++
+
+ if(client)
+ client.sending -= unreceived
+ client.cache |= unreceived
+ client.completed_asset_jobs -= job
+
+ return 1
+
+//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
+//The proc calls procs that sleep for long times.
+proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
+ for(var/file in files)
+ if (!client)
+ break
+ if (register_asset)
+ register_asset(file,files[file])
+ send_asset(client,file)
+ sleep(-1) //queuing calls like this too quickly can cause issues in some client versions
+
+//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
+//if it's an icon or something be careful, you'll have to copy it before further use.
+/proc/register_asset(var/asset_name, var/asset)
+ asset_cache[asset_name] = asset
+
+//From here on out it's populating the asset cache.
+/proc/populate_asset_cache()
+ for(var/type in typesof(/datum/asset) - list(/datum/asset, /datum/asset/simple))
+ var/datum/asset/A = new type()
+ A.register()
+
+ for(var/client/C in clients)
+ //doing this to a client too soon after they've connected can cause issues, also the proc we call sleeps
+ spawn(10)
+ getFilesSlow(C, asset_cache, FALSE)
+
+//These datums are used to populate the asset cache, the proc "register()" does this.
+
+//all of our asset datums, used for referring to these later
+/var/global/list/asset_datums = list()
+
+//get a assetdatum or make a new one
+/proc/get_asset_datum(var/type)
+ if (!(type in asset_datums))
+ return new type()
+ return asset_datums[type]
+
+/datum/asset/New()
+ asset_datums[type] = src
+
+/datum/asset/proc/register()
+ return
+
+/datum/asset/proc/send(client)
+ return
+
+//If you don't need anything complicated.
+/datum/asset/simple
+ var/assets = list()
+ var/verify = FALSE
+
+/datum/asset/simple/register()
+ for(var/asset_name in assets)
+ register_asset(asset_name, assets[asset_name])
+/datum/asset/simple/send(client)
+ send_asset_list(client,assets,verify)
+
+
+//DEFINITIONS FOR ASSET DATUMS START HERE.
+/datum/asset/simple/paper
+ assets = list(
+ "large_stamp-clown.png" = 'icons/paper_icons/large_stamp-clown.png',
+ "large_stamp-deny.png" = 'icons/paper_icons/large_stamp-deny.png',
+ "large_stamp-ok.png" = 'icons/paper_icons/large_stamp-ok.png',
+ "large_stamp-hop.png" = 'icons/paper_icons/large_stamp-hop.png',
+ "large_stamp-cmo.png" = 'icons/paper_icons/large_stamp-cmo.png',
+ "large_stamp-ce.png" = 'icons/paper_icons/large_stamp-ce.png',
+ "large_stamp-hos.png" = 'icons/paper_icons/large_stamp-hos.png',
+ "large_stamp-rd.png" = 'icons/paper_icons/large_stamp-rd.png',
+ "large_stamp-cap.png" = 'icons/paper_icons/large_stamp-cap.png',
+ "large_stamp-qm.png" = 'icons/paper_icons/large_stamp-qm.png',
+ "large_stamp-law.png" = 'icons/paper_icons/large_stamp-law.png',
+ "large_stamp-cent.png" = 'icons/paper_icons/large_stamp-cent.png',
+ "large_stamp-syndicate.png" = 'icons/paper_icons/large_stamp-syndicate.png',
+ "talisman.png" = 'icons/paper_icons/talisman.png',
+ "ntlogo.png" = 'icons/paper_icons/ntlogo.png'
+ )
+
+/datum/asset/nanoui
+ var/list/common = list()
+
+ var/list/common_dirs = list(
+ "nano/css/",
+ "nano/js/",
+ "nano/images/",
+ "nano/layouts/"
+ )
+ var/list/uncommon_dirs = list(
+ "nano/templates/"
+ )
+
+/datum/asset/nanoui/register()
+ // Crawl the directories to find files.
+ for (var/path in common_dirs)
+ var/list/filenames = flist(path)
+ for(var/filename in filenames)
+ if(copytext(filename, length(filename)) != "/") // Ignore directories.
+ if(fexists(path + filename))
+ common[filename] = fcopy_rsc(path + filename)
+ register_asset(filename, common[filename])
+ for (var/path in uncommon_dirs)
+ var/list/filenames = flist(path)
+ for(var/filename in filenames)
+ if(copytext(filename, length(filename)) != "/") // Ignore directories.
+ if(fexists(path + filename))
+ register_asset(filename, fcopy_rsc(path + filename))
+
+/datum/asset/nanoui/send(client, uncommon)
+ if(!islist(uncommon))
+ uncommon = list(uncommon)
+
+ send_asset_list(client, uncommon)
+ send_asset_list(client, common)
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index 784bab5e588..5cd501a0b8f 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -35,6 +35,11 @@
#endif
+ if(href_list["asset_cache_confirm_arrival"])
+ //src << "ASSET JOB [href_list["asset_cache_confirm_arrival"]] ARRIVED."
+ var/job = text2num(href_list["asset_cache_confirm_arrival"])
+ completed_asset_jobs += job
+ return
//Reduces spamming of links by dropping calls that happen during the delay period
if(next_allowed_topic_time > world.time)
@@ -232,8 +237,6 @@
///////////
/client/New(TopicData)
TopicData = null //Prevent calls to client.Topic from connect
- client_cache += src
- client_cache[src] = list()
if(connection != "seeker") //Invalid connection type.
return null
@@ -310,6 +313,9 @@
screen += void
+ if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them.
+ src << "Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you."
+
//////////////
//DISCONNECT//
//////////////
@@ -415,8 +421,7 @@
'html/panels.css' // Used for styling certain panels, such as in the new player panel
)
- // Send NanoUI resources to this client
- spawn nanomanager.send_resources(src)
+ getFilesSlow(src, asset_cache, register_asset = FALSE)
//For debugging purposes
/client/proc/list_all_languages()
diff --git a/code/modules/client/global_cache.dm b/code/modules/client/global_cache.dm
deleted file mode 100644
index f60d26fe84b..00000000000
--- a/code/modules/client/global_cache.dm
+++ /dev/null
@@ -1,84 +0,0 @@
-//We store a list of all clients, with a list of all file names that the client received.
-/var/global/list/client_cache = list()
-
-//List of ALL assets for the above, format is list(filename = asset).
-/var/global/list/asset_cache = list()
-
-//This proc sends the asset to the client, but only if it needs it.
-/proc/send_asset(var/client/client, var/asset_name)
- var/list/client_list = client_cache[client]
- ASSERT(client_list)
-
- if(asset_name in client_list)
- return
-
- //world << "sending a client the asset '[asset_name]'"
- client << browse_rsc(asset_cache[asset_name], asset_name)
- client_list += asset_name
-
-/proc/send_asset_list(var/client/client, var/list/asset_list)
- for(var/asset_name in asset_list)
- send_asset(client, asset_name)
-
-//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
-//if it's an icon or something be careful, you'll have to copy it before further use.
-/proc/register_asset(var/asset_name, var/asset)
- asset_cache |= asset_name
- asset_cache[asset_name] = asset
-
-//From here on out it's populating the asset cache.
-/proc/populate_asset_cache()
- for(var/type in typesof(/datum/asset) - list(/datum/asset, /datum/asset/simple))
- var/datum/asset/A = new type()
-
- A.register()
-
-//These datums are used to populate the asset cache, the proc "register()" does this.
-/datum/asset/proc/register()
- return
-
-//If you don't need anything complicated.
-/datum/asset/simple
- var/assets = list()
-
-/datum/asset/simple/register()
- for(var/asset_name in assets)
- register_asset(asset_name, assets[asset_name])
-
-//DEFINITIONS FOR ASSET DATUMS START HERE.
-/datum/asset/simple/spider_os
- assets = list(
- "sos_1.png" = 'icons/spideros_icons/sos_1.png',
- "sos_2.png" = 'icons/spideros_icons/sos_2.png',
- "sos_3.png" = 'icons/spideros_icons/sos_3.png',
- "sos_4.png" = 'icons/spideros_icons/sos_4.png',
- "sos_5.png" = 'icons/spideros_icons/sos_5.png',
- "sos_6.png" = 'icons/spideros_icons/sos_6.png',
- "sos_7.png" = 'icons/spideros_icons/sos_7.png',
- "sos_8.png" = 'icons/spideros_icons/sos_8.png',
- "sos_9.png" = 'icons/spideros_icons/sos_9.png',
- "sos_10.png" = 'icons/spideros_icons/sos_10.png',
- "sos_11.png" = 'icons/spideros_icons/sos_11.png',
- "sos_12.png" = 'icons/spideros_icons/sos_12.png',
- "sos_13.png" = 'icons/spideros_icons/sos_13.png',
- "sos_14.png" = 'icons/spideros_icons/sos_14.png'
- )
-
-/datum/asset/simple/paper
- assets = list(
- "large_stamp-clown.png" = 'icons/paper_icons/large_stamp-clown.png',
- "large_stamp-deny.png" = 'icons/paper_icons/large_stamp-deny.png',
- "large_stamp-ok.png" = 'icons/paper_icons/large_stamp-ok.png',
- "large_stamp-hop.png" = 'icons/paper_icons/large_stamp-hop.png',
- "large_stamp-cmo.png" = 'icons/paper_icons/large_stamp-cmo.png',
- "large_stamp-ce.png" = 'icons/paper_icons/large_stamp-ce.png',
- "large_stamp-hos.png" = 'icons/paper_icons/large_stamp-hos.png',
- "large_stamp-rd.png" = 'icons/paper_icons/large_stamp-rd.png',
- "large_stamp-cap.png" = 'icons/paper_icons/large_stamp-cap.png',
- "large_stamp-qm.png" = 'icons/paper_icons/large_stamp-qm.png',
- "large_stamp-law.png" = 'icons/paper_icons/large_stamp-law.png',
- "large_stamp-cent.png" = 'icons/paper_icons/large_stamp-cent.png',
- "large_stamp-syndicate.png" = 'icons/paper_icons/large_stamp-syndicate.png',
- "talisman.png" = 'icons/paper_icons/talisman.png',
- "ntlogo.png" = 'icons/paper_icons/ntlogo.png'
- )
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index d301db27226..e7987280d48 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -433,11 +433,11 @@ datum/preferences
dat += "