diff --git a/code/game/gamemodes/scoreboard.dm b/code/game/gamemodes/scoreboard.dm index faba9e82572..bfe8dc0afa4 100644 --- a/code/game/gamemodes/scoreboard.dm +++ b/code/game/gamemodes/scoreboard.dm @@ -1,247 +1,247 @@ -/datum/controller/gameticker/proc/scoreboard() - - //Print a list of antagonists to the server log - var/list/total_antagonists = list() - //Look into all mobs in world, dead or alive - for(var/datum/mind/Mind in minds) - var/temprole = Mind.special_role - if(temprole) //if they are an antagonist of some sort. - if(temprole in total_antagonists) //If the role exists already, add the name to it - total_antagonists[temprole] += ", [Mind.name]([Mind.key])" - else - total_antagonists.Add(temprole) //If the role doesnt exist in the list, create it and add the mob - total_antagonists[temprole] += ": [Mind.name]([Mind.key])" - - //Now print them all into the log! - log_game("Antagonists at round end were...") - for(var/i in total_antagonists) - log_game("[i]s[total_antagonists[i]].") - - // Score Calculation and Display - - // Who is alive/dead, who escaped - for(var/mob/living/silicon/ai/I in mob_list) - if(I.stat == DEAD && (I.z in config.station_levels)) - score_deadaipenalty++ - score_deadcrew++ - - for(var/mob/living/carbon/human/I in mob_list) - if(I.stat == DEAD && (I.z in config.station_levels)) - score_deadcrew++ - - if(I && I.mind) - if(I.mind.assigned_role == "Clown") - for(var/thing in I.attack_log) - if(findtext(thing, "")) //This has to be the hackiest fucking way _ever_ to see attacks. - score_clownabuse++ - - - for(var/mob/living/player in mob_list) - if(player.client) - if (player.stat != DEAD) - var/turf/location = get_turf(player.loc) - var/area/escape_zone = locate(/area/shuttle/escape/centcom) - if(location in escape_zone) - score_escapees++ - - - - var/cash_score = 0 - var/dmg_score = 0 - for(var/mob/living/carbon/human/E in mob_list) - cash_score = 0 - dmg_score = 0 - var/turf/location = get_turf(E.loc) - var/area/escape_zone = locate(/area/shuttle/escape/centcom) - - if(E.stat != DEAD && location in escape_zone) // Escapee Scores - cash_score = get_score_container_worth(E) - - if(cash_score > score_richestcash) - score_richestcash = cash_score - score_richestname = E.real_name - score_richestjob = E.job - score_richestkey = E.key - - dmg_score = E.bruteloss + E.fireloss + E.toxloss + E.oxyloss - if(dmg_score > score_dmgestdamage) - score_dmgestdamage = dmg_score - score_dmgestname = E.real_name - score_dmgestjob = E.job - score_dmgestkey = E.key - - if(ticker && ticker.mode) - ticker.mode.set_scoreboard_gvars() - - - // Check station's power levels - for(var/obj/machinery/power/apc/A in machines) - if(!(A.z in config.station_levels)) continue - - for(var/obj/item/weapon/stock_parts/cell/C in A.contents) - if(C.charge < 2300) - score_powerloss++ //200 charge leeway - - - // Check how much uncleaned mess is on the station - for(var/obj/effect/decal/cleanable/M in world) - if(!(M.z in config.station_levels)) continue - if(istype(M, /obj/effect/decal/cleanable/blood/gibs)) - score_mess += 3 - - if(istype(M, /obj/effect/decal/cleanable/blood)) - score_mess += 1 - - if(istype(M, /obj/effect/decal/cleanable/poop)) - score_mess += 1 - - if(istype(M, /obj/effect/decal/cleanable/vomit)) - score_mess += 1 - - - // Bonus Modifiers - //var/traitorwins = score_traitorswon - var/deathpoints = score_deadcrew * 25 //done - var/researchpoints = score_researchdone * 30 - var/eventpoints = score_eventsendured * 50 - var/escapoints = score_escapees * 25 //done - var/harvests = score_stuffharvested * 5 //done - var/shipping = score_stuffshipped * 5 - var/mining = score_oremined * 2 //done - var/meals = score_meals * 5 //done, but this only counts cooked meals, not drinks served - var/power = score_powerloss * 20 - var/messpoints - if(score_mess != 0) - messpoints = score_mess //done - var/plaguepoints = score_disease * 30 - - - // Good Things - score_crewscore += shipping - score_crewscore += harvests - score_crewscore += mining - score_crewscore += researchpoints - score_crewscore += eventpoints - score_crewscore += escapoints - - if(power == 0) - score_crewscore += 2500 - score_powerbonus = 1 - - if(score_mess == 0) - score_crewscore += 3000 - score_messbonus = 1 - - - score_crewscore += meals - if(score_allarrested) - score_crewscore *= 3 // This needs to be here for the bonus to be applied properly - - - score_crewscore -= deathpoints - if(score_deadaipenalty) - score_crewscore -= 250 - score_crewscore -= power - - - score_crewscore -= messpoints - score_crewscore -= plaguepoints - - // Show the score - might add "ranks" later - world << "The crew's final score is:" - world << "[score_crewscore]" - for(var/mob/E in player_list) - if(E.client) - if(E.client.prefs && !(E.client.prefs.toggles & DISABLE_SCOREBOARD)) - E.scorestats() - -// A recursive function to properly determine the wealthiest escapee -/datum/controller/gameticker/proc/get_score_container_worth(atom/C, level=0) - if(level >= 5) - // in case the containers recurse or something - return 0 - else - . = 0 - for(var/obj/item/weapon/card/id/id in C.contents) - var/datum/money_account/A = get_money_account(id.associated_account_number) - // has an account? - if(A) - . += A.money - for(var/obj/item/weapon/spacecash/cash in C.contents) - . += cash.get_total() - for(var/obj/item/weapon/storage/S in C.contents) - . += .(S, level + 1) - -/datum/game_mode/proc/get_scoreboard_stats() - return null - -/datum/game_mode/proc/set_scoreboard_gvars() - return null - -/mob/proc/scorestats() - var/dat = "Round Statistics and Score

" - if(ticker && ticker.mode) - dat += ticker.mode.get_scoreboard_stats() - - dat += {" - General Statistics
- The Good:
- - Useful Items Shipped: [score_stuffshipped] ([score_stuffshipped * 5] Points)
- Hydroponics Harvests: [score_stuffharvested] ([score_stuffharvested * 5] Points)
- Ore Mined: [score_oremined] ([score_oremined * 2] Points)
- Refreshments Prepared: [score_meals] ([score_meals * 5] Points)
- Research Completed: [score_researchdone] ([score_researchdone * 30] Points)
"} - if (!emergency_shuttle.location()) dat += "Shuttle Escapees: [score_escapees] ([score_escapees * 25] Points)
" - dat += {"Random Events Endured: [score_eventsendured] ([score_eventsendured * 50] Points)
- Whole Station Powered: [score_powerbonus ? "Yes" : "No"] ([score_powerbonus * 2500] Points)
- Ultra-Clean Station: [score_mess ? "No" : "Yes"] ([score_messbonus * 3000] Points)

- The bad:
- - Dead bodies on Station: [score_deadcrew] (-[score_deadcrew * 25] Points)
- Uncleaned Messes: [score_mess] (-[score_mess] Points)
- Station Power Issues: [score_powerloss] (-[score_powerloss * 20] Points)
- Rampant Diseases: [score_disease] (-[score_disease * 30] Points)
- AI Destroyed: [score_deadaipenalty ? "Yes" : "No"] (-[score_deadaipenalty * 250] Points)

- The Weird
- - Food Eaten: [score_foodeaten]
- Times a Clown was Abused: [score_clownabuse]

- "} - if (score_escapees) - dat += {"Richest Escapee: [score_richestname], [score_richestjob]: $[num2text(score_richestcash,50)] ([score_richestkey])
- Most Battered Escapee: [score_dmgestname], [score_dmgestjob]: [score_dmgestdamage] damage ([score_dmgestkey])
"} - else - if(emergency_shuttle.location()) - dat += "The station wasn't evacuated!
" - else - dat += "No-one escaped!
" - - dat += ticker.mode.declare_job_completion() - - dat += {" -

- FINAL SCORE: [score_crewscore]
- "} - - var/score_rating = "The Aristocrats!" - switch(score_crewscore) - if(-99999 to -50000) score_rating = "Even the Singularity Deserves Better" - if(-49999 to -5000) score_rating = "Singularity Fodder" - if(-4999 to -1000) score_rating = "You're All Fired" - if(-999 to -500) score_rating = "A Waste of Perfectly Good Oxygen" - if(-499 to -250) score_rating = "A Wretched Heap of Scum and Incompetence" - if(-249 to -100) score_rating = "Outclassed by Lab Monkeys" - if(-99 to -21) score_rating = "The Undesirables" - if(-20 to 20) score_rating = "Ambivalently Average" - if(21 to 99) score_rating = "Not Bad, but Not Good" - if(100 to 249) score_rating = "Skillful Servants of Science" - if(250 to 499) score_rating = "Best of a Good Bunch" - if(500 to 999) score_rating = "Lean Mean Machine Thirteen" - if(1000 to 4999) score_rating = "Promotions for Everyone" - if(5000 to 9999) score_rating = "Ambassadors of Discovery" - if(10000 to 49999) score_rating = "The Pride of Science Itself" - if(50000 to INFINITY) score_rating = "Nanotrasen's Finest" - - dat += "RATING: [score_rating]" +/datum/controller/gameticker/proc/scoreboard() + + //Print a list of antagonists to the server log + var/list/total_antagonists = list() + //Look into all mobs in world, dead or alive + for(var/datum/mind/Mind in minds) + var/temprole = Mind.special_role + if(temprole) //if they are an antagonist of some sort. + if(temprole in total_antagonists) //If the role exists already, add the name to it + total_antagonists[temprole] += ", [Mind.name]([Mind.key])" + else + total_antagonists.Add(temprole) //If the role doesnt exist in the list, create it and add the mob + total_antagonists[temprole] += ": [Mind.name]([Mind.key])" + + //Now print them all into the log! + log_game("Antagonists at round end were...") + for(var/i in total_antagonists) + log_game("[i]s[total_antagonists[i]].") + + // Score Calculation and Display + + // Who is alive/dead, who escaped + for(var/mob/living/silicon/ai/I in mob_list) + if(I.stat == DEAD && (I.z in config.station_levels)) + score_deadaipenalty++ + score_deadcrew++ + + for(var/mob/living/carbon/human/I in mob_list) + if(I.stat == DEAD && (I.z in config.station_levels)) + score_deadcrew++ + + if(I && I.mind) + if(I.mind.assigned_role == "Clown") + for(var/thing in I.attack_log) + if(findtext(thing, "")) //This has to be the hackiest fucking way _ever_ to see attacks. + score_clownabuse++ + + + for(var/mob/living/player in mob_list) + if(player.client) + if (player.stat != DEAD) + var/turf/location = get_turf(player.loc) + var/area/escape_zone = locate(/area/shuttle/escape/centcom) + if(location in escape_zone) + score_escapees++ + + + + var/cash_score = 0 + var/dmg_score = 0 + for(var/mob/living/carbon/human/E in mob_list) + cash_score = 0 + dmg_score = 0 + var/turf/location = get_turf(E.loc) + var/area/escape_zone = locate(/area/shuttle/escape/centcom) + + if(E.stat != DEAD && location in escape_zone) // Escapee Scores + cash_score = get_score_container_worth(E) + + if(cash_score > score_richestcash) + score_richestcash = cash_score + score_richestname = E.real_name + score_richestjob = E.job + score_richestkey = E.key + + dmg_score = E.bruteloss + E.fireloss + E.toxloss + E.oxyloss + if(dmg_score > score_dmgestdamage) + score_dmgestdamage = dmg_score + score_dmgestname = E.real_name + score_dmgestjob = E.job + score_dmgestkey = E.key + + if(ticker && ticker.mode) + ticker.mode.set_scoreboard_gvars() + + + // Check station's power levels + for(var/obj/machinery/power/apc/A in machines) + if(!(A.z in config.station_levels)) continue + + for(var/obj/item/weapon/stock_parts/cell/C in A.contents) + if(C.charge < 2300) + score_powerloss++ //200 charge leeway + + + // Check how much uncleaned mess is on the station + for(var/obj/effect/decal/cleanable/M in world) + if(!(M.z in config.station_levels)) continue + if(istype(M, /obj/effect/decal/cleanable/blood/gibs)) + score_mess += 3 + + if(istype(M, /obj/effect/decal/cleanable/blood)) + score_mess += 1 + + if(istype(M, /obj/effect/decal/cleanable/poop)) + score_mess += 1 + + if(istype(M, /obj/effect/decal/cleanable/vomit)) + score_mess += 1 + + + // Bonus Modifiers + //var/traitorwins = score_traitorswon + var/deathpoints = score_deadcrew * 25 //done + var/researchpoints = score_researchdone * 30 + var/eventpoints = score_eventsendured * 50 + var/escapoints = score_escapees * 25 //done + var/harvests = score_stuffharvested * 5 //done + var/shipping = score_stuffshipped * 5 + var/mining = score_oremined * 2 //done + var/meals = score_meals * 5 //done, but this only counts cooked meals, not drinks served + var/power = score_powerloss * 20 + var/messpoints + if(score_mess != 0) + messpoints = score_mess //done + var/plaguepoints = score_disease * 30 + + + // Good Things + score_crewscore += shipping + score_crewscore += harvests + score_crewscore += mining + score_crewscore += researchpoints + score_crewscore += eventpoints + score_crewscore += escapoints + + if(power == 0) + score_crewscore += 2500 + score_powerbonus = 1 + + if(score_mess == 0) + score_crewscore += 3000 + score_messbonus = 1 + + + score_crewscore += meals + if(score_allarrested) + score_crewscore *= 3 // This needs to be here for the bonus to be applied properly + + + score_crewscore -= deathpoints + if(score_deadaipenalty) + score_crewscore -= 250 + score_crewscore -= power + + + score_crewscore -= messpoints + score_crewscore -= plaguepoints + + // Show the score - might add "ranks" later + world << "The crew's final score is:" + world << "[score_crewscore]" + for(var/mob/E in player_list) + if(E.client) + if(E.client.prefs && !(E.client.prefs.toggles & DISABLE_SCOREBOARD)) + E.scorestats() + +// A recursive function to properly determine the wealthiest escapee +/datum/controller/gameticker/proc/get_score_container_worth(atom/C, level=0) + if(level >= 5) + // in case the containers recurse or something + return 0 + else + . = 0 + for(var/obj/item/weapon/card/id/id in C.contents) + var/datum/money_account/A = get_money_account(id.associated_account_number) + // has an account? + if(A) + . += A.money + for(var/obj/item/weapon/spacecash/cash in C.contents) + . += cash.get_total() + for(var/obj/item/weapon/storage/S in C.contents) + . += .(S, level + 1) + +/datum/game_mode/proc/get_scoreboard_stats() + return null + +/datum/game_mode/proc/set_scoreboard_gvars() + return null + +/mob/proc/scorestats() + var/dat = "Round Statistics and Score

" + if(ticker && ticker.mode) + dat += ticker.mode.get_scoreboard_stats() + + dat += {" + General Statistics
+ The Good:
+ + Useful Items Shipped: [score_stuffshipped] ([score_stuffshipped * 5] Points)
+ Hydroponics Harvests: [score_stuffharvested] ([score_stuffharvested * 5] Points)
+ Ore Mined: [score_oremined] ([score_oremined * 2] Points)
+ Refreshments Prepared: [score_meals] ([score_meals * 5] Points)
+ Research Completed: [score_researchdone] ([score_researchdone * 30] Points)
"} + if (!emergency_shuttle.location()) dat += "Shuttle Escapees: [score_escapees] ([score_escapees * 25] Points)
" + dat += {"Random Events Endured: [score_eventsendured] ([score_eventsendured * 50] Points)
+ Whole Station Powered: [score_powerbonus ? "Yes" : "No"] ([score_powerbonus * 2500] Points)
+ Ultra-Clean Station: [score_mess ? "No" : "Yes"] ([score_messbonus * 3000] Points)

+ The bad:
+ + Dead bodies on Station: [score_deadcrew] (-[score_deadcrew * 25] Points)
+ Uncleaned Messes: [score_mess] (-[score_mess] Points)
+ Station Power Issues: [score_powerloss] (-[score_powerloss * 20] Points)
+ Rampant Diseases: [score_disease] (-[score_disease * 30] Points)
+ AI Destroyed: [score_deadaipenalty ? "Yes" : "No"] (-[score_deadaipenalty * 250] Points)

+ The Weird
+ + Food Eaten: [score_foodeaten]
+ Times a Clown was Abused: [score_clownabuse]

+ "} + if (score_escapees) + dat += {"Richest Escapee: [score_richestname], [score_richestjob]: $[num2text(score_richestcash,50)] ([score_richestkey])
+ Most Battered Escapee: [score_dmgestname], [score_dmgestjob]: [score_dmgestdamage] damage ([score_dmgestkey])
"} + else + if(emergency_shuttle.location()) + dat += "The station wasn't evacuated!
" + else + dat += "No-one escaped!
" + + dat += ticker.mode.declare_job_completion() + + dat += {" +

+ FINAL SCORE: [score_crewscore]
+ "} + + var/score_rating = "The Aristocrats!" + switch(score_crewscore) + if(-99999 to -50000) score_rating = "Even the Singularity Deserves Better" + if(-49999 to -5000) score_rating = "Singularity Fodder" + if(-4999 to -1000) score_rating = "You're All Fired" + if(-999 to -500) score_rating = "A Waste of Perfectly Good Oxygen" + if(-499 to -250) score_rating = "A Wretched Heap of Scum and Incompetence" + if(-249 to -100) score_rating = "Outclassed by Lab Monkeys" + if(-99 to -21) score_rating = "The Undesirables" + if(-20 to 20) score_rating = "Ambivalently Average" + if(21 to 99) score_rating = "Not Bad, but Not Good" + if(100 to 249) score_rating = "Skillful Servants of Science" + if(250 to 499) score_rating = "Best of a Good Bunch" + if(500 to 999) score_rating = "Lean Mean Machine Thirteen" + if(1000 to 4999) score_rating = "Promotions for Everyone" + if(5000 to 9999) score_rating = "Ambassadors of Discovery" + if(10000 to 49999) score_rating = "The Pride of Science Itself" + if(50000 to INFINITY) score_rating = "Nanotrasen's Finest" + + dat += "RATING: [score_rating]" src << browse(dat, "window=roundstats;size=500x600") \ No newline at end of file diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 2459e1cc121..0035170d4dd 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -7,6 +7,8 @@ icon = 'icons/obj/Cryogenic2.dmi' icon_state = "console" var/obj/machinery/sleeper/connected = null + var/ui_title = "Sleeper" + anchored = 1 //About time someone fixed this. density = 1 var/orient = "LEFT" @@ -39,10 +41,6 @@ /obj/machinery/sleep_console/RefreshParts() /obj/machinery/sleep_console/process() - if(stat & (NOPOWER|BROKEN)) - return - src.updateUsrDialog() - return /obj/machinery/sleep_console/ex_act(severity) switch(severity) @@ -116,88 +114,137 @@ findsleeper() if (src.connected) - var/mob/living/occupant = src.connected.occupant - var/dat = "Occupant Statistics:
" - if (occupant) - var/t1 - switch(occupant.stat) - if(0) - t1 = "Conscious" - if(1) - t1 = "Unconscious" - if(2) - t1 = "*dead*" - else - dat += text("[]\tHealth %: [] ([])

", (occupant.health > 50 ? "" : ""), occupant.health, t1) - if(iscarbon(occupant)) - var/mob/living/carbon/C = occupant - dat += text("[]\t-Pulse, bpm: []
", (C.pulse == PULSE_NONE || C.pulse == PULSE_THREADY ? "" : ""), C.get_pulse(GETPULSE_TOOL)) - dat += text("[]\t-Brute Damage %: []
", (occupant.getBruteLoss() < 60 ? "" : ""), occupant.getBruteLoss()) - dat += text("[]\t-Respiratory Damage %: []
", (occupant.getOxyLoss() < 60 ? "" : ""), occupant.getOxyLoss()) - dat += text("[]\t-Toxin Content %: []
", (occupant.getToxLoss() < 60 ? "" : ""), occupant.getToxLoss()) - dat += text("[]\t-Burn Severity %: []
", (occupant.getFireLoss() < 60 ? "" : ""), occupant.getFireLoss()) - dat += text("
Paralysis Summary %: [] ([] seconds left!)
", occupant.paralysis, round(occupant.paralysis / 4)) - if(occupant.reagents) - for(var/chemical in connected.injection_chems) - var/datum/reagent/C = chemical_reagents_list[chemical] - dat += "[C.name]: [occupant.reagents.get_reagent_amount(chemical)] units
" - dat += "Refresh Meter Readings
" - if(src.connected.beaker) - dat += "
Remove Beaker
" - if(src.connected.filtering) - dat += "Stop Dialysis
" - dat += text("Output Beaker has [] units of free space remaining

", src.connected.beaker.reagents.maximum_volume - src.connected.beaker.reagents.total_volume) - else - dat += "
Start Dialysis
" - dat += text("Output Beaker has [] units of free space remaining

", src.connected.beaker.reagents.maximum_volume - src.connected.beaker.reagents.total_volume) - else - dat += "
No Dialysis Output Beaker is present.

" - for(var/chemical in connected.injection_chems) - var/datum/reagent/C = chemical_reagents_list[chemical] - dat += "Inject [C.name]: " - for(var/amount in connected.amounts) - dat += "[amount] units
" - dat += "
Eject Patient" - else - dat += "The sleeper is empty." - dat += text("

Close", user) - user << browse(dat, "window=sleeper;size=400x500") - onclose(user, "sleeper") - return + ui_interact(user) + +/obj/machinery/sleep_console/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + var/mob/living/carbon/human/occupant = connected.occupant + data["hasOccupant"] = occupant ? 1 : 0 + var/occupantData[0] + var/crisis = 0 + if (occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = config.health_threshold_dead + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["paralysis"] = occupant.paralysis + occupantData["hasBlood"] = 0 + occupantData["bodyTemperature"] = occupant.bodytemperature + occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations + // Because we can put simple_animals in here, we need to do something tricky to get things working nice + occupantData["temperatureSuitability"] = 0 // 0 is the baseline + if (ishuman(occupant) && occupant.species) + // I wanna do something where the bar gets bluer as the temperature gets lower + // For now, I'll just use the standard format for the temperature status + var/datum/species/sp = occupant.species + if (occupant.bodytemperature < sp.cold_level_3) + occupantData["temperatureSuitability"] = -3 + else if (occupant.bodytemperature < sp.cold_level_2) + occupantData["temperatureSuitability"] = -2 + else if (occupant.bodytemperature < sp.cold_level_1) + occupantData["temperatureSuitability"] = -1 + else if (occupant.bodytemperature > sp.heat_level_3) + occupantData["temperatureSuitability"] = 3 + else if (occupant.bodytemperature > sp.heat_level_2) + occupantData["temperatureSuitability"] = 2 + else if (occupant.bodytemperature > sp.heat_level_1) + occupantData["temperatureSuitability"] = 1 + else if (istype(occupant, /mob/living/simple_animal)) + var/mob/living/simple_animal/silly = occupant + if (silly.bodytemperature < silly.minbodytemp) + occupantData["temperatureSuitability"] = -3 + else if (silly.bodytemperature > silly.maxbodytemp) + occupantData["temperatureSuitability"] = 3 + // Blast you, imperial measurement system + occupantData["btCelsius"] = occupant.bodytemperature - T0C + occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 + + + crisis = (occupant.health < connected.min_health) + // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent + // Runtime is aptly named, isn't she? + if (ishuman(occupant) && occupant.vessel && !(occupant.species && occupant.species.flags & NO_BLOOD)) + occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) + occupantData["hasBlood"] = 1 + occupantData["bloodLevel"] = round(occupant.vessel.get_reagent_amount("blood")) + occupantData["bloodMax"] = occupant.max_blood + occupantData["bloodPercent"] = round(100*(occupant.vessel.get_reagent_amount("blood")/occupant.max_blood), 0.01) + + data["occupant"] = occupantData + data["maxchem"] = connected.max_chem + data["minhealth"] = connected.min_health + data["dialysis"] = connected.filtering + if (connected.beaker) + data["isBeakerLoaded"] = 1 + data["beakerFreeSpace"] = round(connected.beaker.reagents.maximum_volume - connected.beaker.reagents.total_volume) + + var/chemicals[0] + for (var/re in connected.injection_chems) + var/datum/reagent/temp = chemical_reagents_list[re] + if(temp) + var/reagent_amount = 0 + var/pretty_amount + var/injectable = occupant ? 1 : 0 + var/overdosing = 0 + var/caution = 0 // To make things clear that you're coming close to an overdose + if (crisis && !(temp.id in connected.emergency_chems)) + injectable = 0 + + if (occupant && occupant.reagents) + reagent_amount = occupant.reagents.get_reagent_amount(temp.id) + // If they're mashing the highest concentration, they get one warning + if (temp.overdose_threshold && reagent_amount + 10 > temp.overdose_threshold) + caution = 1 + if (temp.id in occupant.reagents.overdose_list()) + overdosing = 1 + + // Because I don't know how to do this on the nano side + pretty_amount = round(reagent_amount, 0.05) + + chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) + data["chemicals"] = chemicals + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "sleeper.tmpl", ui_title, 550, 655) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) /obj/machinery/sleep_console/Topic(href, href_list) + if(!connected | usr == connected.occupant) + return 0 + if(..()) return 1 if(panel_open) usr << "Close the maintenance panel first." - return 1 + return 0 if ((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) - usr.set_machine(src) if (href_list["chemical"]) if (src.connected) if (src.connected.occupant) if (src.connected.occupant.stat == DEAD) - usr << "\red \b This person has no life for to preserve anymore. Take them to a department capable of reanimating them." - else if(src.connected.occupant.health > src.connected.min_health || href_list["chemical"] == "epinephrine") + usr << "This person has no life for to preserve anymore. Take them to a department capable of reanimating them." + else if(src.connected.occupant.health > src.connected.min_health || (href_list["chemical"] in connected.emergency_chems)) src.connected.inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"])) else - usr << "\red \b This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!" - src.updateUsrDialog() - if (href_list["refresh"]) - src.updateUsrDialog() + usr << "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!" if (href_list["removebeaker"]) src.connected.remove_beaker() - src.updateUsrDialog() if (href_list["togglefilter"]) src.connected.toggle_filter() - src.updateUsrDialog() if (href_list["ejectify"]) src.connected.eject() - src.updateUsrDialog() src.add_fingerprint(usr) - return + return 1 ///////////////////////////////////////// // THE SLEEPER ITSELF @@ -216,24 +263,25 @@ list("epinephrine", "ether", "salbutamol", "styptic_powder", "oculine"), list("epinephrine", "ether", "salbutamol", "styptic_powder", "oculine", "charcoal", "mutadone", "mannitol"), list("epinephrine", "ether", "salbutamol", "styptic_powder", "oculine", "charcoal", "mutadone", "mannitol", "pen_acid", "omnizine")) + var/emergency_chems = list("epinephrine") // Desnowflaking var/amounts = list(5, 10) var/obj/item/weapon/reagent_containers/glass/beaker = null var/filtering = 0 - var/efficiency + var/max_chem var/initial_bin_rating = 1 - var/min_health = 25 + var/min_health = -25 var/injection_chems = list() idle_power_usage = 1250 active_power_usage = 2500 light_color = LIGHT_COLOR_CYAN - power_change() - ..() - if(!(stat & (BROKEN|NOPOWER))) - set_light(2) - else - set_light(0) +/obj/machinery/sleeper/power_change() + ..() + if(!(stat & (BROKEN|NOPOWER))) + set_light(2) + else + set_light(0) /obj/machinery/sleeper/New() ..() @@ -271,16 +319,20 @@ I += M.rating injection_chems = possible_chems[I] - efficiency = E + max_chem = E * 20 min_health = -E * 25 /obj/machinery/sleeper/process() if(filtering > 0) if(beaker) + // To prevent runtimes from drawing blood from runtime, and to prevent getting IPC blood. + if(!istype(occupant) || !occupant.dna || (occupant.species && occupant.species.flags & NO_BLOOD)) + filtering = 0 + return + if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) src.occupant.vessel.trans_to(beaker, 1) for(var/datum/reagent/x in src.occupant.reagents.reagent_list) - // world << "FILTERING CHEMS" src.occupant.reagents.trans_to(beaker, 3) src.occupant.vessel.trans_to(beaker, 1) src.updateDialog() @@ -306,11 +358,10 @@ beaker = G G.forceMove(src) user.visible_message("[user] adds \a [G] to \the [src]!", "You add \a [G] to \the [src]!") - src.updateUsrDialog() return else - user << "\red The sleeper has a beaker already." + user << "The sleeper has a beaker already." return if (istype(G, /obj/item/weapon/screwdriver)) @@ -342,12 +393,12 @@ if(istype(G, /obj/item/weapon/grab)) if(panel_open) - user << "\blue Close the maintenance panel first." + user << "Close the maintenance panel first." return if(!ismob(G:affecting)) return if(src.occupant) - user << "\blue The sleeper is already occupied!" + user << "The sleeper is already occupied!" return for(var/mob/living/carbon/slime/M in range(1,G:affecting)) if(M.Victim == G:affecting) @@ -358,7 +409,7 @@ if(do_after(user, 20, target = G:affecting)) if(src.occupant) - user << "\blue The sleeper is already occupied!" + user << "The sleeper is already occupied!" return if(!G || !G:affecting) return var/mob/M = G:affecting @@ -368,7 +419,7 @@ M.forceMove(src) src.occupant = M src.icon_state = "sleeper" - M << "\blue You feel cool air surround you. You go numb as your senses turn inward." + M << "You feel cool air surround you. You go numb as your senses turn inward." src.add_fingerprint(user) qdel(G) @@ -412,6 +463,8 @@ go_out() ..(severity) +// ??? +// This looks cool, although mildly broken, should it be included again? /obj/machinery/sleeper/alter_health(mob/living/M as mob) if (M.health > 0) if (M.getOxyLoss() >= 10) @@ -447,11 +500,14 @@ return /obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount) + if (!(chemical in injection_chems)) + user << "The sleeper does not offer that chemical!" + return + if(src.occupant) if(src.occupant.reagents) - if(src.occupant.reagents.get_reagent_amount(chemical) + amount <= 20 * efficiency) + if(src.occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) src.occupant.reagents.add_reagent(chemical, amount) - user << "Occupant now has [src.occupant.reagents.get_reagent_amount(chemical)] units of [chemical] in his/her bloodstream." return else user << "You can not inject any more of this chemical." @@ -463,39 +519,9 @@ user << "There's no occupant in the sleeper!" return - -/obj/machinery/sleeper/proc/check(mob/living/user as mob) - if(src.occupant) - user << text("\blue Occupant ([]) Statistics:", src.occupant) - var/t1 - switch(src.occupant.stat) - if(0.0) - t1 = "Conscious" - if(1.0) - t1 = "Unconscious" - if(2.0) - t1 = "*dead*" - else - user << text("[]\t Health %: [] ([])", (src.occupant.health > 50 ? "\blue " : "\red "), src.occupant.health, t1) - user << text("[]\t -Core Temperature: []°C ([]°F)

", (src.occupant.bodytemperature > 50 ? "" : ""), src.occupant.bodytemperature-T0C, src.occupant.bodytemperature*1.8-459.67) - user << text("[]\t -Brute Damage %: []", (src.occupant.getBruteLoss() < 60 ? "\blue " : "\red "), src.occupant.getBruteLoss()) - user << text("[]\t -Respiratory Damage %: []", (src.occupant.getOxyLoss() < 60 ? "\blue " : "\red "), src.occupant.getOxyLoss()) - user << text("[]\t -Toxin Content %: []", (src.occupant.getToxLoss() < 60 ? "\blue " : "\red "), src.occupant.getToxLoss()) - user << text("[]\t -Burn Severity %: []", (src.occupant.getFireLoss() < 60 ? "\blue " : "\red "), src.occupant.getFireLoss()) - user << "\blue Expected time till occupant can safely awake: (note: If health is below 20% these times are inaccurate)" - user << text("\blue \t [] second\s (if around 1 or 2 the sleeper is keeping them asleep.)", src.occupant.paralysis / 5) - if(src.beaker) - user << text("\blue \t Dialysis Output Beaker has [] of free space remaining.", src.beaker.reagents.maximum_volume - src.beaker.reagents.total_volume) - else - user << "\blue No Dialysis Output Beaker loaded." - else - user << "\blue There is no one inside!" - return - - /obj/machinery/sleeper/verb/eject() set name = "Eject Sleeper" - set category = null + set category = "Object" set src in oview(1) if(usr.stat != 0) return @@ -506,7 +532,7 @@ /obj/machinery/sleeper/verb/remove_beaker() set name = "Remove Beaker" - set category = null + set category = "Object" set src in oview(1) if(usr.stat != 0) return @@ -520,7 +546,7 @@ /obj/machinery/sleeper/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) if(O.loc == user) //no you can't pull things out of your ass return - if(user.restrained() || user.stat || user.weakened || user.stunned || user.paralysis || user.resting) //are you cuffed, dying, lying, stunned or other + if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other return if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source return @@ -535,10 +561,10 @@ if(!istype(user.loc, /turf) || !istype(O.loc, /turf)) // are you in a container/closet/pod/etc? return if(panel_open) - user << "\blue Close the maintenance panel first." + user << "Close the maintenance panel first." return if(occupant) - user << "\blue The sleeper is already occupied!" + user << "The sleeper is already occupied!" return /* if(isrobot(user)) if(!istype(user:module, /obj/item/weapon/robot_module/medical)) @@ -548,7 +574,7 @@ if(!istype(L) || L.buckled) return if(L.abiotic()) - user << "\blue Subject cannot have abiotic items on." + user << "Subject cannot have abiotic items on." return for(var/mob/living/carbon/slime/M in range(1,L)) if(M.Victim == L) @@ -561,7 +587,7 @@ if(do_after(user, 20, target = L)) if(src.occupant) - user << "\blue The sleeper is already occupied!" + user << ">The sleeper is already occupied!" return if(!L) return @@ -571,7 +597,7 @@ L.forceMove(src) src.occupant = L src.icon_state = "sleeper" - L << "\blue You feel cool air surround you. You go numb as your senses turn inward." + L << "You feel cool air surround you. You go numb as your senses turn inward." src.add_fingerprint(user) if(user.pulling == L) user.pulling = null @@ -583,15 +609,15 @@ /obj/machinery/sleeper/verb/move_inside() set name = "Enter Sleeper" - set category = null + set category = "Object" set src in oview(1) if(usr.stat != 0 || !(ishuman(usr))) return if(src.occupant) - usr << "\blue The sleeper is already occupied!" + usr << "The sleeper is already occupied!" return if (panel_open) - usr << "\blue Close the maintenance panel first." + usr << "Close the maintenance panel first." return if(usr.restrained() || usr.stat || usr.weakened || usr.stunned || usr.paralysis || usr.resting) //are you cuffed, dying, lying, stunned or other return @@ -602,7 +628,7 @@ visible_message("[usr] starts climbing into the sleeper.") if(do_after(usr, 20, target = usr)) if(src.occupant) - usr << "\blue The sleeper is already occupied!" + usr << "The sleeper is already occupied!" return usr.stop_pulling() usr.client.perspective = EYE_PERSPECTIVE diff --git a/code/game/machinery/kitchen/monkeyrecycler.dm b/code/game/machinery/kitchen/monkeyrecycler.dm index 22115ad0cdc..810143f6583 100644 --- a/code/game/machinery/kitchen/monkeyrecycler.dm +++ b/code/game/machinery/kitchen/monkeyrecycler.dm @@ -53,11 +53,11 @@ var/mob/living/carbon/human/target = grabbed if(issmall(target)) if(target.stat == 0) - user << "\red The monkey is struggling far too much to put it in the recycler." + user << "The monkey is struggling far too much to put it in the recycler." else user.drop_item() qdel(target) - user << "\blue You stuff the monkey in the machine." + user << "You stuff the monkey in the machine." playsound(src.loc, 'sound/machines/juicer.ogg', 50, 1) var/offset = prob(50) ? -2 : 2 animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 200) //start shaking @@ -65,22 +65,23 @@ src.grinded++ sleep(50) pixel_x = initial(pixel_x) - user << "\blue The machine now has [grinded] monkeys worth of material stored." + user << "The machine now has [grinded] monkey\s worth of material stored." else - user << "\red The machine only accepts monkeys!" + user << "The machine only accepts monkeys!" else - user << "\red The machine only accepts monkeys!" + user << "The machine only accepts monkeys!" return /obj/machinery/monkey_recycler/attack_hand(var/mob/user as mob) if (src.stat != 0) //NOPOWER etc return if(grinded >= required_grind) - user << "\blue The machine hisses loudly as it condenses the grinded monkey meat. After a moment, it dispenses a brand new monkey cube." + user << "The machine hisses loudly as it condenses the grinded monkey meat. After a moment, it dispenses a brand new monkey cube." playsound(src.loc, 'sound/machines/hiss.ogg', 50, 1) grinded -= required_grind - new /obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped(src.loc) - user << "\blue The machine's display flashes that it has [grinded] monkeys worth of material left." - else - user << "\red The machine needs at least [required_grind] monkey\s worth of material to produce a monkey cube. It only has [grinded]." - return + for(var/i = 0, i < cube_production, i++) // Forgot to fix this bit the first time through + new /obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped(src.loc) + user << "The machine's display flashes that it has [grinded] monkey\s worth of material left." + else // I'm not sure if the \s macro works with a word in between; I'll play it safe + user << "The machine needs at least [required_grind] monkey\s worth of material to compress [cube_production] monkey\s. It only has [grinded]." + return \ No newline at end of file diff --git a/code/game/objects/items/weapons/cash.dm b/code/game/objects/items/weapons/cash.dm index 294a697454a..be8cc69f39f 100644 --- a/code/game/objects/items/weapons/cash.dm +++ b/code/game/objects/items/weapons/cash.dm @@ -1,111 +1,111 @@ -var/global/list/moneytypes=list( - /obj/item/weapon/spacecash/c1000 = 1000, - /obj/item/weapon/spacecash/c500 = 500, // Might get rid of this. - /obj/item/weapon/spacecash/c100 = 100, - /obj/item/weapon/spacecash/c10 = 10, - /obj/item/weapon/spacecash = 1, -) - -/obj/item/weapon/spacecash - name = "credit chip" - desc = "Money money money." - gender = PLURAL - icon = 'icons/obj/money.dmi' - icon_state = "cash1" - opacity = 0 - density = 0 - anchored = 0.0 - force = 1.0 - throwforce = 1.0 - throw_speed = 1 - throw_range = 2 - w_class = 1.0 - var/access = list() - access = access_crate_cash - var/worth = 1 // Per chip - var/amount = 1 // number of chips - var/stack_color = "#4E054F" - -/obj/item/weapon/spacecash/New(var/new_loc,var/new_amount=1) - loc = new_loc - name = "[worth] credit chip" - amount = new_amount - update_icon() - -/obj/item/weapon/spacecash/examine(mob/user) - if(amount>1) - user << "\icon[src] This is a stack of [amount] [src]s." - else - user << "\icon[src] This is \a [src]s." - user << "It's worth [worth*amount] credits." - -/obj/item/weapon/spacecash/update_icon() - icon_state = "cash[worth]" - // Up to 100 items per stack. - overlays = 0 - var/stacksize=round(amount/25) - pixel_x=rand(-7,7) - pixel_y=rand(-14,14) - if(stacksize) - // 0 = single - // 1 = 1/4 stack - // 2 = 1/2 stack - // 3 = 3/4 stack - // 4 = full stack - var/image/stack = image(icon,icon_state="cashstack[stacksize]") - stack.color=stack_color - overlays += stack - -/obj/item/weapon/spacecash/proc/get_total() - return worth * amount - -/obj/item/weapon/spacecash/c10 - icon_state = "cash10" - worth = 10 - stack_color = "#663200" - -/obj/item/weapon/spacecash/c20 - icon_state = "cash10" - worth = 20 - stack_color = "#663200" - -/obj/item/weapon/spacecash/c50 - icon_state = "cash10" - worth = 50 - stack_color = "#663200" - -/obj/item/weapon/spacecash/c100 - icon_state = "cash100" - worth = 100 - stack_color = "#663200" - -/obj/item/weapon/spacecash/c200 - icon_state = "cash200" - worth = 200 - stack_color = "#663200" - -/obj/item/weapon/spacecash/c500 - icon_state = "cash500" - worth = 500 - stack_color = "#663200" - -/obj/item/weapon/spacecash/c1000 - icon_state = "cash1000" - worth = 1000 - stack_color = "#333333" - -/proc/dispense_cash(var/amount, var/loc) - for(var/cashtype in moneytypes) - var/slice = moneytypes[cashtype] - var/dispense_count = Floor(amount/slice) - amount = amount % slice - while(dispense_count>0) - var/dispense_this_time = min(dispense_count,100) - if(dispense_this_time > 0) - new cashtype(loc,dispense_this_time) - dispense_count -= dispense_this_time - -/proc/count_cash(var/list/cash) - . = 0 - for(var/obj/item/weapon/spacecash/C in cash) +var/global/list/moneytypes=list( + /obj/item/weapon/spacecash/c1000 = 1000, + /obj/item/weapon/spacecash/c500 = 500, // Might get rid of this. + /obj/item/weapon/spacecash/c100 = 100, + /obj/item/weapon/spacecash/c10 = 10, + /obj/item/weapon/spacecash = 1, +) + +/obj/item/weapon/spacecash + name = "credit chip" + desc = "Money money money." + gender = PLURAL + icon = 'icons/obj/money.dmi' + icon_state = "cash1" + opacity = 0 + density = 0 + anchored = 0.0 + force = 1.0 + throwforce = 1.0 + throw_speed = 1 + throw_range = 2 + w_class = 1.0 + var/access = list() + access = access_crate_cash + var/worth = 1 // Per chip + var/amount = 1 // number of chips + var/stack_color = "#4E054F" + +/obj/item/weapon/spacecash/New(var/new_loc,var/new_amount=1) + loc = new_loc + name = "[worth] credit chip" + amount = new_amount + update_icon() + +/obj/item/weapon/spacecash/examine(mob/user) + if(amount>1) + user << "\icon[src] This is a stack of [amount] [src]s." + else + user << "\icon[src] This is \a [src]s." + user << "It's worth [worth*amount] credits." + +/obj/item/weapon/spacecash/update_icon() + icon_state = "cash[worth]" + // Up to 100 items per stack. + overlays = 0 + var/stacksize=round(amount/25) + pixel_x=rand(-7,7) + pixel_y=rand(-14,14) + if(stacksize) + // 0 = single + // 1 = 1/4 stack + // 2 = 1/2 stack + // 3 = 3/4 stack + // 4 = full stack + var/image/stack = image(icon,icon_state="cashstack[stacksize]") + stack.color=stack_color + overlays += stack + +/obj/item/weapon/spacecash/proc/get_total() + return worth * amount + +/obj/item/weapon/spacecash/c10 + icon_state = "cash10" + worth = 10 + stack_color = "#663200" + +/obj/item/weapon/spacecash/c20 + icon_state = "cash10" + worth = 20 + stack_color = "#663200" + +/obj/item/weapon/spacecash/c50 + icon_state = "cash10" + worth = 50 + stack_color = "#663200" + +/obj/item/weapon/spacecash/c100 + icon_state = "cash100" + worth = 100 + stack_color = "#663200" + +/obj/item/weapon/spacecash/c200 + icon_state = "cash200" + worth = 200 + stack_color = "#663200" + +/obj/item/weapon/spacecash/c500 + icon_state = "cash500" + worth = 500 + stack_color = "#663200" + +/obj/item/weapon/spacecash/c1000 + icon_state = "cash1000" + worth = 1000 + stack_color = "#333333" + +/proc/dispense_cash(var/amount, var/loc) + for(var/cashtype in moneytypes) + var/slice = moneytypes[cashtype] + var/dispense_count = Floor(amount/slice) + amount = amount % slice + while(dispense_count>0) + var/dispense_this_time = min(dispense_count,100) + if(dispense_this_time > 0) + new cashtype(loc,dispense_this_time) + dispense_count -= dispense_this_time + +/proc/count_cash(var/list/cash) + . = 0 + for(var/obj/item/weapon/spacecash/C in cash) . += C.get_total() \ No newline at end of file diff --git a/code/game/vehicles/spacepods/spacepod.dm b/code/game/vehicles/spacepods/spacepod.dm index d1525fde451..effd1bd64b9 100644 --- a/code/game/vehicles/spacepods/spacepod.dm +++ b/code/game/vehicles/spacepods/spacepod.dm @@ -85,6 +85,7 @@ pr_give_air = null qdel(ion_trail) ion_trail = null + occupant_sanity_check() if(occupant) occupant.forceMove(get_turf(src)) occupant = null @@ -148,6 +149,7 @@ var/oldhealth = health health = max(0, health - damage) var/percentage = (health / initial(health)) * 100 + occupant_sanity_check() if(occupant && oldhealth > health && percentage <= 25 && percentage > 0) var/sound/S = sound('sound/effects/engine_alert2.ogg') S.wait = 0 //No queue @@ -186,6 +188,7 @@ /obj/spacepod/ex_act(severity) + occupant_sanity_check() switch(severity) if(1) var/mob/living/carbon/human/H = occupant @@ -207,6 +210,7 @@ deal_damage(50) /obj/spacepod/emp_act(severity) + occupant_sanity_check() switch(severity) if(1) if(src.occupant) src.occupant << "The pod console flashes 'Heavy EMP WAVE DETECTED'." //warn the occupants @@ -464,129 +468,118 @@ . = t_air.return_temperature() return -/obj/spacepod/proc/moved_inside(var/mob/living/carbon/human/H as mob) - var/fukkendisk = usr.GetTypeInAllContents(/obj/item/weapon/disk/nuclear) - if(fukkendisk) - usr << "\red The nuke-disk locks the door as you try to get in. You evil person." - return - - if(H && H.client && H in range(1)) - if(src.occupant && src.occupant2) - H << "[src.name] is full." - return - - if(src.occupant && !src.occupant2) - if(src.occupant == H) - H.visible_message("You climb over the console and drop down into the secondary seat.") - H.reset_view(src) - H.stop_pulling() - H.forceMove(src) - if(src.occupant == H) occupant = null - src.occupant2 = H - src.add_fingerprint(H) - src.forceMove(src.loc) - playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - return 1 - - else - if(!src.occupant) - H.reset_view(src) - H.stop_pulling() - H.forceMove(src) - src.occupant = H - src.add_fingerprint(H) - src.forceMove(src.loc) - playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - return 1 - else - return - else - return 0 - /obj/spacepod/proc/moved_other_inside(var/mob/living/carbon/human/H as mob) - if(!src.occupant2) - H.reset_view(src) + occupant_sanity_check() + if(!occupant2) H.stop_pulling() H.forceMove(src) - src.occupant2 = H - src.forceMove(src.loc) + occupant2 = H + H.forceMove(src) playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) return 1 - else + +/obj/spacepod/MouseDrop_T(mob/M, mob/user) + if(!isliving(M)) return - - -/obj/spacepod/MouseDrop_T(mob/M as mob, mob/user as mob) - if(!isliving(M)) return + occupant_sanity_check() if(M != user && M.stat == DEAD && allow2enter) - if(src.occupant2 && !src.occupant) + if(occupant2 && !occupant) usr << "You can't put a corpse into the driver's seat!" - return - if(!src.occupant2) + return 0 + if(!occupant2) visible_message("[user.name] starts loading [M.name] into the pod!") sleep(10) moved_other_inside(M) if(M == user) - move_inside(user) + enter_pod(user) -/obj/spacepod/verb/move_inside(var/mob/user) +/obj/spacepod/verb/enter_pod(mob/user = usr) set category = "Object" set name = "Enter Pod" set src in oview(1) - var/fukkendisk = usr.GetTypeInAllContents(/obj/item/weapon/disk/nuclear) - if(user.restrained() || user.stat || user.weakened || user.stunned || user.paralysis || user.resting) //are you cuffed, dying, lying, stunned or other - return - if (!ishuman(user)) return + if(!istype(user)) + return 0 + + var/fukkendisk = user.GetTypeInAllContents(/obj/item/weapon/disk/nuclear) + + if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other + return 0 + if(!ishuman(user)) + return 0 if(fukkendisk) - user << "The nuke-disk is locking the door every time you try to open it. You get the feeling that it doesn't want to go into the spacepod." - return + user << "The nuke-disk is locking the door every time you try to open it. You get the feeling that it doesn't want to go into the spacepod." + return 0 for(var/mob/living/carbon/slime/S in range(1,usr)) if(S.Victim == user) user << "You're too busy getting your life sucked out of you." - return + return 0 - try_user_enter(user) + move_inside(user) -/obj/spacepod/proc/try_user_enter(var/mob/user) - if(!src.occupant && !src.occupant2) - visible_message("[user] starts to climb into [src.name]") - if(enter_after(40,user)) - if(!src.occupant) - moved_inside(user) - else if(src.occupant != user) - user << "[src.occupant] was faster. Try better next time, loser." - else user << "You stop entering the spacepod." - return +/obj/spacepod/proc/move_inside(mob/user) + if(!istype(user)) + log_debug("SHIT'S GONE WRONG WITH THE SPACEPOD [src] AT [x], [y], [z], AREA [get_area(src)], TURF [get_turf(src)]") - if(!allow2enter) - user << "The door is locked!" - return + occupant_sanity_check() - if(occupant && occupant2) - user << "You can't fit in \the [src], it's full!" - return + if(!occupant) + visible_message("[user] starts to climb into \the [src].") + if(do_after(user, 40, target = src)) + if(!occupant) + user.stop_pulling() + occupant = user + user.forceMove(src) + add_fingerprint(user) + playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) + else + user << "[occupant] was faster. Try better next time, loser." + else + user << "You stop entering \the [src]." - if(occupant && !occupant2) - user << "You start climbing into the passenger bay." - user_enter(user) - - if(!occupant && occupant2) - user << "You start climbing into the pilot's seat." - user_enter(user) - -/obj/spacepod/proc/user_enter(var/mob/user) - visible_message("[user] starts to climb into \the [src.name].") - if(enter_after(40,user)) - moved_inside(user) + else if(!occupant2) + visible_message("[user] starts to climb into \the [src].") + if(do_after(user, 40, target = src)) + if(!occupant2) + user.stop_pulling() + occupant2 = user + user.forceMove(src) + add_fingerprint(user) + playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) + else + user << "[occupant] was faster. Try better next time, loser." + else + user << "You stop entering \the [src]." else - user << "You stop entering the spacepod." + user << "You can't fit in \the [src], it's full!" + +/obj/spacepod/proc/occupant_sanity_check() + if(occupant) + if(!ismob(occupant)) + occupant.forceMove(get_turf(src)) + log_debug("##SPACEPOD WARNING: NON-MOB OCCUPANT [occupant], TURF [get_turf(src)] | AREA [get_area(src)] | COORDS [x], [y], [z]") + occupant = null + else if(occupant.loc != src) + log_debug("##SPACEPOD WARNING: OCCUPANT [occupant] ESCAPED, TURF [get_turf(src)] | AREA [get_area(src)] | COORDS [x], [y], [z]") + occupant = null + if(occupant2) + if(!ismob(occupant2)) + occupant2.forceMove(get_turf(src)) + log_debug("##SPACEPOD WARNING: NON-MOB OCCUPANT [occupant2], TURF [get_turf(src)] | AREA [get_area(src)] | COORDS [x], [y], [z]") + occupant2 = null + else if(occupant2.loc != src) + log_debug("##SPACEPOD WARNING: OCCUPANT [occupant2] ESCAPED, TURF [get_turf(src)] | AREA [get_area(src)] | COORDS [x], [y], [z]") + occupant2 = null + + if(!occupant && !allow2enter) + allow2enter = 1 + log_debug("##SPACEPOD WARNING: DOORS WERE STILL LOCKED WITH NO OCCUPANT, TURF [get_turf(src)] | AREA [get_area(src)] | COORDS [x], [y], [z]") /obj/spacepod/verb/exit_pod() set name = "Exit pod" @@ -595,7 +588,10 @@ var/mob/user = usr var/spos = 0 - if(!ismob(user)) return + if(!istype(user)) + return + + occupant_sanity_check() if(occupant == user) spos = 1 if(occupant2 == user) spos = 2 @@ -618,6 +614,8 @@ set category = "Spacepod" set src = usr.loc + occupant_sanity_check() + if(!occupant2) usr << "There is no one in the second seat." return @@ -685,7 +683,7 @@ return equipment_system.weapon_system.fire_weapons() -obj/spacepod/verb/toggleLights() +/obj/spacepod/verb/toggleLights() set name = "Toggle Lights" set category = "Spacepod" set src = usr.loc diff --git a/code/modules/economy/ATM.dm b/code/modules/economy/ATM.dm index 4e16a1325a0..31ad003135e 100644 --- a/code/modules/economy/ATM.dm +++ b/code/modules/economy/ATM.dm @@ -1,402 +1,402 @@ -/* - -TODO: -give money an actual use (QM stuff, vending machines) -send money to people (might be worth attaching money to custom database thing for this, instead of being in the ID) -log transactions - -*/ - -#define NO_SCREEN 0 -#define CHANGE_SECURITY_LEVEL 1 -#define TRANSFER_FUNDS 2 -#define VIEW_TRANSACTION_LOGS 3 -#define PRINT_DELAY 100 - -/obj/machinery/atm - name = "Nanotrasen Automatic Teller Machine" - desc = "For all your monetary needs!" - icon = 'icons/obj/terminals.dmi' - icon_state = "atm" - anchored = 1 - use_power = 1 - idle_power_usage = 10 - var/obj/machinery/computer/account_database/linked_db - var/datum/money_account/authenticated_account - var/number_incorrect_tries = 0 - var/previous_account_number = 0 - var/max_pin_attempts = 3 - var/ticks_left_locked_down = 0 - var/ticks_left_timeout = 0 - var/machine_id = "" - var/obj/item/weapon/card/held_card - var/editing_security_level = 0 - var/view_screen = NO_SCREEN - var/lastprint = 0 // Printer needs time to cooldown - -/obj/machinery/atm/New() - ..() - machine_id = "[station_name()] RT #[num_financial_terminals++]" - -/obj/machinery/atm/initialize() - ..() - reconnect_database() - -/obj/machinery/atm/process() - if(stat & NOPOWER) - return - - if(linked_db && ( (linked_db.stat & NOPOWER) || !linked_db.activated ) ) - linked_db = null - authenticated_account = null - src.visible_message("\red \icon[src] [src] buzzes rudely, \"Connection to remote database lost.\"") - updateDialog() - - if(ticks_left_timeout > 0) - ticks_left_timeout-- - if(ticks_left_timeout <= 0) - authenticated_account = null - if(ticks_left_locked_down > 0) - ticks_left_locked_down-- - if(ticks_left_locked_down <= 0) - number_incorrect_tries = 0 - - if(authenticated_account) - var/turf/T = get_turf(src) - if(istype(T) && locate(/obj/item/weapon/spacecash) in T) - var/list/cash_found = list() - for(var/obj/item/weapon/spacecash/S in T) - cash_found+=S - if(cash_found.len>0) - if(prob(50)) - playsound(loc, 'sound/items/polaroid1.ogg', 50, 1) - else - playsound(loc, 'sound/items/polaroid2.ogg', 50, 1) - var/amount = count_cash(cash_found) - for(var/obj/item/weapon/spacecash/S in cash_found) - qdel(S) - authenticated_account.charge(-amount,null,"Credit deposit",terminal_id=machine_id,dest_name = "Terminal") - -/obj/machinery/atm/proc/reconnect_database() - for(var/obj/machinery/computer/account_database/DB in world) //Hotfix until someone finds out why it isn't in 'machines' - if( DB.z == src.z && !(DB.stat & NOPOWER) && DB.activated ) - linked_db = DB - break - -/obj/machinery/atm/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I, /obj/item/weapon/card)) - var/obj/item/weapon/card/id/idcard = I - if(!held_card) - usr.drop_item() - idcard.loc = src - held_card = idcard - if(authenticated_account && held_card.associated_account_number != authenticated_account.account_number) - authenticated_account = null - else if(authenticated_account) - if(istype(I,/obj/item/weapon/spacecash)) - //consume the money - var/obj/item/weapon/spacecash/C = I - authenticated_account.money += C.get_total() - if(prob(50)) - playsound(loc, 'sound/items/polaroid1.ogg', 50, 1) - else - playsound(loc, 'sound/items/polaroid2.ogg', 50, 1) - - //create a transaction log entry - var/datum/transaction/T = new() - T.target_name = authenticated_account.owner_name - T.purpose = "Credit deposit" - T.amount = C.get_total() - T.source_terminal = machine_id - T.date = current_date_string - T.time = worldtime2text() - authenticated_account.transaction_log.Add(T) - - user << "You insert [C] into [src]." - src.attack_hand(user) - qdel(I) - else - ..() - -/obj/machinery/atm/attack_hand(mob/user as mob) - if(istype(user, /mob/living/silicon)) - user << "\red Artificial unit recognized. Artificial units do not currently receive monetary compensation, as per Nanotrasen regulation #1005." - return - if(get_dist(src,user) <= 1) - //check to see if the user has low security enabled - scan_user(user) - - //js replicated from obj/machinery/computer/card - var/dat = {"

Nanotrasen Automatic Teller Machine

- For all your monetary needs!
- This terminal is [machine_id]. Report this code when contacting Nanotrasen IT Support
- Card: [held_card ? held_card.name : "------"]

"} - - if(ticks_left_locked_down > 0) - dat += "Maximum number of pin attempts exceeded! Access to this ATM has been temporarily disabled." - else if(authenticated_account) - switch(view_screen) - if(CHANGE_SECURITY_LEVEL) - dat += "Select a new security level for this account:

" - var/text = "Zero - Either the account number or card is required to access this account. EFTPOS transactions will require a card and ask for a pin, but not verify the pin is correct." - if(authenticated_account.security_level != 0) - text = "[text]" - dat += "[text]
" - text = "One - An account number and pin must be manually entered to access this account and process transactions." - if(authenticated_account.security_level != 1) - text = "[text]" - dat += "[text]
" - text = "Two - In addition to account number and pin, a card is required to access this account and process transactions." - if(authenticated_account.security_level != 2) - text = "[text]" - dat += {"[text]

- Back"} - if(VIEW_TRANSACTION_LOGS) - dat += {"Transaction logs
- Back - - - - - - - - - "} - for(var/datum/transaction/T in authenticated_account.transaction_log) - dat += {" - - - - - - - "} - dat += "
DateTimeTargetPurposeValueSource terminal ID
[T.date][T.time][T.target_name][T.purpose]$[T.amount][T.source_terminal]
" - if(TRANSFER_FUNDS) - dat += {"Account balance: $[authenticated_account.money]
- Back

-
- - - Target account number:
- Funds to transfer:
- Transaction purpose:
-
-
"} - else - dat += {"Welcome, [authenticated_account.owner_name].
- Account balance: $[authenticated_account.money] -
- - -
-
- Change account security level
- Make transfer
- View transaction log
- Print balance statement
- Logout
"} - else if(linked_db) - dat += {"
- - - Account:
- PIN:
-
-
"} - else - dat += "Unable to connect to accounts database, please retry and if the issue persists contact Nanotrasen IT support." - reconnect_database() - - user << browse(dat,"window=atm;size=550x650") - else - user << browse(null,"window=atm") - -/obj/machinery/atm/Topic(var/href, var/href_list) - if(href_list["choice"]) - switch(href_list["choice"]) - if("transfer") - if(authenticated_account && linked_db) - var/transfer_amount = text2num(href_list["funds_amount"]) - if(transfer_amount <= 0) - alert("That is not a valid amount.") - else if(transfer_amount <= authenticated_account.money) - var/target_account_number = text2num(href_list["target_acc_number"]) - var/transfer_purpose = href_list["purpose"] - if(linked_db.charge_to_account(target_account_number, authenticated_account.owner_name, transfer_purpose, machine_id, transfer_amount)) - usr << "\icon[src]Funds transfer successful." - authenticated_account.money -= transfer_amount - - //create an entry in the account transaction log - var/datum/transaction/T = new() - T.target_name = "Account #[target_account_number]" - T.purpose = transfer_purpose - T.source_terminal = machine_id - T.date = current_date_string - T.time = worldtime2text() - T.amount = "([transfer_amount])" - authenticated_account.transaction_log.Add(T) - else - usr << "\icon[src]Funds transfer failed." - - else - usr << "\icon[src]You don't have enough funds to do that!" - if("view_screen") - view_screen = text2num(href_list["view_screen"]) - if("change_security_level") - if(authenticated_account) - var/new_sec_level = max( min(text2num(href_list["new_security_level"]), 2), 0) - authenticated_account.security_level = new_sec_level - if("attempt_auth") - if(linked_db && !ticks_left_locked_down) - var/tried_account_num = text2num(href_list["account_num"]) - if(!tried_account_num) - tried_account_num = held_card.associated_account_number - var/tried_pin = text2num(href_list["account_pin"]) - - authenticated_account = attempt_account_access(tried_account_num, tried_pin, held_card && held_card.associated_account_number == tried_account_num ? 2 : 1) - if(!authenticated_account) - number_incorrect_tries++ - if(previous_account_number == tried_account_num) - if(number_incorrect_tries > max_pin_attempts) - //lock down the atm - ticks_left_locked_down = 30 - playsound(src, 'sound/machines/buzz-two.ogg', 50, 1) - - //create an entry in the account transaction log - var/datum/money_account/failed_account = linked_db.get_account(tried_account_num) - if(failed_account) - var/datum/transaction/T = new() - T.target_name = failed_account.owner_name - T.purpose = "Unauthorised login attempt" - T.source_terminal = machine_id - T.date = current_date_string - T.time = worldtime2text() - failed_account.transaction_log.Add(T) - else - usr << "\red \icon[src] Incorrect pin/account combination entered, [max_pin_attempts - number_incorrect_tries] attempts remaining." - previous_account_number = tried_account_num - playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 1) - else - usr << "\red \icon[src] incorrect pin/account combination entered." - number_incorrect_tries = 0 - else - playsound(src, 'sound/machines/twobeep.ogg', 50, 1) - ticks_left_timeout = 120 - view_screen = NO_SCREEN - - //create a transaction log entry - var/datum/transaction/T = new() - T.target_name = authenticated_account.owner_name - T.purpose = "Remote terminal access" - T.source_terminal = machine_id - T.date = current_date_string - T.time = worldtime2text() - authenticated_account.transaction_log.Add(T) - - usr << "\blue \icon[src] Access granted. Welcome user '[authenticated_account.owner_name].'" - - previous_account_number = tried_account_num - if("withdrawal") - var/amount = max(text2num(href_list["funds_amount"]),0) - if(amount <= 0) - alert("That is not a valid amount.") - else if(authenticated_account && amount > 0) - if(amount <= authenticated_account.money) - playsound(src, 'sound/machines/chime.ogg', 50, 1) - - //remove the money - if(amount > 10000) // prevent crashes - usr << "\blue The ATM's screen flashes, 'Maximum single withdrawl limit reached, defaulting to 10,000.'" - amount = 10000 - authenticated_account.money -= amount - withdraw_arbitrary_sum(amount) - - //create an entry in the account transaction log - var/datum/transaction/T = new() - T.target_name = authenticated_account.owner_name - T.purpose = "Credit withdrawal" - T.amount = "([amount])" - T.source_terminal = machine_id - T.date = current_date_string - T.time = worldtime2text() - authenticated_account.transaction_log.Add(T) - else - usr << "\icon[src]You don't have enough funds to do that!" - if("balance_statement") - if(authenticated_account) - if(world.timeofday < lastprint + PRINT_DELAY) - usr << "The [src.name] flashes an error on its display." - return - lastprint = world.timeofday - var/obj/item/weapon/paper/R = new(src.loc) - R.name = "Account balance: [authenticated_account.owner_name]" - R.info = {"NT Automated Teller Account Statement

- Account holder: [authenticated_account.owner_name]
- Account number: [authenticated_account.account_number]
- Balance: $[authenticated_account.money]
- Date and time: [worldtime2text()], [current_date_string]

- Service terminal ID: [machine_id]
"} - - //stamp the paper - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - stampoverlay.icon_state = "paper_stamp-cent" - if(!R.stamped) - R.stamped = new - R.stamped += /obj/item/weapon/stamp - R.overlays += stampoverlay - R.stamps += "
This paper has been stamped by the Automatic Teller Machine." - - if(prob(50)) - playsound(loc, 'sound/items/polaroid1.ogg', 50, 1) - else - playsound(loc, 'sound/items/polaroid2.ogg', 50, 1) - if("insert_card") - if(held_card) - held_card.loc = src.loc - authenticated_account = null - - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(held_card) - held_card = null - - else - var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/weapon/card/id)) - usr.drop_item() - I.loc = src - held_card = I - if("logout") - authenticated_account = null - //usr << browse(null,"window=atm") - - src.attack_hand(usr) - -//create the most effective combination of notes to make up the requested amount -/obj/machinery/atm/proc/withdraw_arbitrary_sum(var/arbitrary_sum) - dispense_cash(arbitrary_sum,get_step(get_turf(src),turn(dir,180))) // Spawn on the ATM. - -//stolen wholesale and then edited a bit from newscasters, which are awesome and by Agouri -/obj/machinery/atm/proc/scan_user(mob/living/carbon/human/human_user as mob) - if(!authenticated_account && linked_db) - if(human_user.wear_id) - var/obj/item/weapon/card/id/I - if(istype(human_user.wear_id, /obj/item/weapon/card/id) ) - I = human_user.wear_id - else if(istype(human_user.wear_id, /obj/item/device/pda) ) - var/obj/item/device/pda/P = human_user.wear_id - I = P.id - if(I) - authenticated_account = attempt_account_access(I.associated_account_number) - if(authenticated_account) - human_user << "\blue \icon[src] Access granted. Welcome user '[authenticated_account.owner_name].'" - - //create a transaction log entry - var/datum/transaction/T = new() - T.target_name = authenticated_account.owner_name - T.purpose = "Remote terminal access" - T.source_terminal = machine_id - T.date = current_date_string - T.time = worldtime2text() - authenticated_account.transaction_log.Add(T) +/* + +TODO: +give money an actual use (QM stuff, vending machines) +send money to people (might be worth attaching money to custom database thing for this, instead of being in the ID) +log transactions + +*/ + +#define NO_SCREEN 0 +#define CHANGE_SECURITY_LEVEL 1 +#define TRANSFER_FUNDS 2 +#define VIEW_TRANSACTION_LOGS 3 +#define PRINT_DELAY 100 + +/obj/machinery/atm + name = "Nanotrasen Automatic Teller Machine" + desc = "For all your monetary needs!" + icon = 'icons/obj/terminals.dmi' + icon_state = "atm" + anchored = 1 + use_power = 1 + idle_power_usage = 10 + var/obj/machinery/computer/account_database/linked_db + var/datum/money_account/authenticated_account + var/number_incorrect_tries = 0 + var/previous_account_number = 0 + var/max_pin_attempts = 3 + var/ticks_left_locked_down = 0 + var/ticks_left_timeout = 0 + var/machine_id = "" + var/obj/item/weapon/card/held_card + var/editing_security_level = 0 + var/view_screen = NO_SCREEN + var/lastprint = 0 // Printer needs time to cooldown + +/obj/machinery/atm/New() + ..() + machine_id = "[station_name()] RT #[num_financial_terminals++]" + +/obj/machinery/atm/initialize() + ..() + reconnect_database() + +/obj/machinery/atm/process() + if(stat & NOPOWER) + return + + if(linked_db && ( (linked_db.stat & NOPOWER) || !linked_db.activated ) ) + linked_db = null + authenticated_account = null + src.visible_message("\red \icon[src] [src] buzzes rudely, \"Connection to remote database lost.\"") + updateDialog() + + if(ticks_left_timeout > 0) + ticks_left_timeout-- + if(ticks_left_timeout <= 0) + authenticated_account = null + if(ticks_left_locked_down > 0) + ticks_left_locked_down-- + if(ticks_left_locked_down <= 0) + number_incorrect_tries = 0 + + if(authenticated_account) + var/turf/T = get_turf(src) + if(istype(T) && locate(/obj/item/weapon/spacecash) in T) + var/list/cash_found = list() + for(var/obj/item/weapon/spacecash/S in T) + cash_found+=S + if(cash_found.len>0) + if(prob(50)) + playsound(loc, 'sound/items/polaroid1.ogg', 50, 1) + else + playsound(loc, 'sound/items/polaroid2.ogg', 50, 1) + var/amount = count_cash(cash_found) + for(var/obj/item/weapon/spacecash/S in cash_found) + qdel(S) + authenticated_account.charge(-amount,null,"Credit deposit",terminal_id=machine_id,dest_name = "Terminal") + +/obj/machinery/atm/proc/reconnect_database() + for(var/obj/machinery/computer/account_database/DB in world) //Hotfix until someone finds out why it isn't in 'machines' + if( DB.z == src.z && !(DB.stat & NOPOWER) && DB.activated ) + linked_db = DB + break + +/obj/machinery/atm/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I, /obj/item/weapon/card)) + var/obj/item/weapon/card/id/idcard = I + if(!held_card) + usr.drop_item() + idcard.loc = src + held_card = idcard + if(authenticated_account && held_card.associated_account_number != authenticated_account.account_number) + authenticated_account = null + else if(authenticated_account) + if(istype(I,/obj/item/weapon/spacecash)) + //consume the money + var/obj/item/weapon/spacecash/C = I + authenticated_account.money += C.get_total() + if(prob(50)) + playsound(loc, 'sound/items/polaroid1.ogg', 50, 1) + else + playsound(loc, 'sound/items/polaroid2.ogg', 50, 1) + + //create a transaction log entry + var/datum/transaction/T = new() + T.target_name = authenticated_account.owner_name + T.purpose = "Credit deposit" + T.amount = C.get_total() + T.source_terminal = machine_id + T.date = current_date_string + T.time = worldtime2text() + authenticated_account.transaction_log.Add(T) + + user << "You insert [C] into [src]." + src.attack_hand(user) + qdel(I) + else + ..() + +/obj/machinery/atm/attack_hand(mob/user as mob) + if(istype(user, /mob/living/silicon)) + user << "\red Artificial unit recognized. Artificial units do not currently receive monetary compensation, as per Nanotrasen regulation #1005." + return + if(get_dist(src,user) <= 1) + //check to see if the user has low security enabled + scan_user(user) + + //js replicated from obj/machinery/computer/card + var/dat = {"

Nanotrasen Automatic Teller Machine

+ For all your monetary needs!
+ This terminal is [machine_id]. Report this code when contacting Nanotrasen IT Support
+ Card: [held_card ? held_card.name : "------"]

"} + + if(ticks_left_locked_down > 0) + dat += "Maximum number of pin attempts exceeded! Access to this ATM has been temporarily disabled." + else if(authenticated_account) + switch(view_screen) + if(CHANGE_SECURITY_LEVEL) + dat += "Select a new security level for this account:

" + var/text = "Zero - Either the account number or card is required to access this account. EFTPOS transactions will require a card and ask for a pin, but not verify the pin is correct." + if(authenticated_account.security_level != 0) + text = "[text]" + dat += "[text]
" + text = "One - An account number and pin must be manually entered to access this account and process transactions." + if(authenticated_account.security_level != 1) + text = "[text]" + dat += "[text]
" + text = "Two - In addition to account number and pin, a card is required to access this account and process transactions." + if(authenticated_account.security_level != 2) + text = "[text]" + dat += {"[text]

+ Back"} + if(VIEW_TRANSACTION_LOGS) + dat += {"Transaction logs
+ Back + + + + + + + + + "} + for(var/datum/transaction/T in authenticated_account.transaction_log) + dat += {" + + + + + + + "} + dat += "
DateTimeTargetPurposeValueSource terminal ID
[T.date][T.time][T.target_name][T.purpose]$[T.amount][T.source_terminal]
" + if(TRANSFER_FUNDS) + dat += {"Account balance: $[authenticated_account.money]
+ Back

+
+ + + Target account number:
+ Funds to transfer:
+ Transaction purpose:
+
+
"} + else + dat += {"Welcome, [authenticated_account.owner_name].
+ Account balance: $[authenticated_account.money] +
+ + +
+
+ Change account security level
+ Make transfer
+ View transaction log
+ Print balance statement
+ Logout
"} + else if(linked_db) + dat += {"
+ + + Account:
+ PIN:
+
+
"} + else + dat += "Unable to connect to accounts database, please retry and if the issue persists contact Nanotrasen IT support." + reconnect_database() + + user << browse(dat,"window=atm;size=550x650") + else + user << browse(null,"window=atm") + +/obj/machinery/atm/Topic(var/href, var/href_list) + if(href_list["choice"]) + switch(href_list["choice"]) + if("transfer") + if(authenticated_account && linked_db) + var/transfer_amount = text2num(href_list["funds_amount"]) + if(transfer_amount <= 0) + alert("That is not a valid amount.") + else if(transfer_amount <= authenticated_account.money) + var/target_account_number = text2num(href_list["target_acc_number"]) + var/transfer_purpose = href_list["purpose"] + if(linked_db.charge_to_account(target_account_number, authenticated_account.owner_name, transfer_purpose, machine_id, transfer_amount)) + usr << "\icon[src]Funds transfer successful." + authenticated_account.money -= transfer_amount + + //create an entry in the account transaction log + var/datum/transaction/T = new() + T.target_name = "Account #[target_account_number]" + T.purpose = transfer_purpose + T.source_terminal = machine_id + T.date = current_date_string + T.time = worldtime2text() + T.amount = "([transfer_amount])" + authenticated_account.transaction_log.Add(T) + else + usr << "\icon[src]Funds transfer failed." + + else + usr << "\icon[src]You don't have enough funds to do that!" + if("view_screen") + view_screen = text2num(href_list["view_screen"]) + if("change_security_level") + if(authenticated_account) + var/new_sec_level = max( min(text2num(href_list["new_security_level"]), 2), 0) + authenticated_account.security_level = new_sec_level + if("attempt_auth") + if(linked_db && !ticks_left_locked_down) + var/tried_account_num = text2num(href_list["account_num"]) + if(!tried_account_num) + tried_account_num = held_card.associated_account_number + var/tried_pin = text2num(href_list["account_pin"]) + + authenticated_account = attempt_account_access(tried_account_num, tried_pin, held_card && held_card.associated_account_number == tried_account_num ? 2 : 1) + if(!authenticated_account) + number_incorrect_tries++ + if(previous_account_number == tried_account_num) + if(number_incorrect_tries > max_pin_attempts) + //lock down the atm + ticks_left_locked_down = 30 + playsound(src, 'sound/machines/buzz-two.ogg', 50, 1) + + //create an entry in the account transaction log + var/datum/money_account/failed_account = linked_db.get_account(tried_account_num) + if(failed_account) + var/datum/transaction/T = new() + T.target_name = failed_account.owner_name + T.purpose = "Unauthorised login attempt" + T.source_terminal = machine_id + T.date = current_date_string + T.time = worldtime2text() + failed_account.transaction_log.Add(T) + else + usr << "\red \icon[src] Incorrect pin/account combination entered, [max_pin_attempts - number_incorrect_tries] attempts remaining." + previous_account_number = tried_account_num + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 1) + else + usr << "\red \icon[src] incorrect pin/account combination entered." + number_incorrect_tries = 0 + else + playsound(src, 'sound/machines/twobeep.ogg', 50, 1) + ticks_left_timeout = 120 + view_screen = NO_SCREEN + + //create a transaction log entry + var/datum/transaction/T = new() + T.target_name = authenticated_account.owner_name + T.purpose = "Remote terminal access" + T.source_terminal = machine_id + T.date = current_date_string + T.time = worldtime2text() + authenticated_account.transaction_log.Add(T) + + usr << "\blue \icon[src] Access granted. Welcome user '[authenticated_account.owner_name].'" + + previous_account_number = tried_account_num + if("withdrawal") + var/amount = max(text2num(href_list["funds_amount"]),0) + if(amount <= 0) + alert("That is not a valid amount.") + else if(authenticated_account && amount > 0) + if(amount <= authenticated_account.money) + playsound(src, 'sound/machines/chime.ogg', 50, 1) + + //remove the money + if(amount > 10000) // prevent crashes + usr << "\blue The ATM's screen flashes, 'Maximum single withdrawl limit reached, defaulting to 10,000.'" + amount = 10000 + authenticated_account.money -= amount + withdraw_arbitrary_sum(amount) + + //create an entry in the account transaction log + var/datum/transaction/T = new() + T.target_name = authenticated_account.owner_name + T.purpose = "Credit withdrawal" + T.amount = "([amount])" + T.source_terminal = machine_id + T.date = current_date_string + T.time = worldtime2text() + authenticated_account.transaction_log.Add(T) + else + usr << "\icon[src]You don't have enough funds to do that!" + if("balance_statement") + if(authenticated_account) + if(world.timeofday < lastprint + PRINT_DELAY) + usr << "The [src.name] flashes an error on its display." + return + lastprint = world.timeofday + var/obj/item/weapon/paper/R = new(src.loc) + R.name = "Account balance: [authenticated_account.owner_name]" + R.info = {"NT Automated Teller Account Statement

+ Account holder: [authenticated_account.owner_name]
+ Account number: [authenticated_account.account_number]
+ Balance: $[authenticated_account.money]
+ Date and time: [worldtime2text()], [current_date_string]

+ Service terminal ID: [machine_id]
"} + + //stamp the paper + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + stampoverlay.icon_state = "paper_stamp-cent" + if(!R.stamped) + R.stamped = new + R.stamped += /obj/item/weapon/stamp + R.overlays += stampoverlay + R.stamps += "
This paper has been stamped by the Automatic Teller Machine." + + if(prob(50)) + playsound(loc, 'sound/items/polaroid1.ogg', 50, 1) + else + playsound(loc, 'sound/items/polaroid2.ogg', 50, 1) + if("insert_card") + if(held_card) + held_card.loc = src.loc + authenticated_account = null + + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(held_card) + held_card = null + + else + var/obj/item/I = usr.get_active_hand() + if (istype(I, /obj/item/weapon/card/id)) + usr.drop_item() + I.loc = src + held_card = I + if("logout") + authenticated_account = null + //usr << browse(null,"window=atm") + + src.attack_hand(usr) + +//create the most effective combination of notes to make up the requested amount +/obj/machinery/atm/proc/withdraw_arbitrary_sum(var/arbitrary_sum) + dispense_cash(arbitrary_sum,get_step(get_turf(src),turn(dir,180))) // Spawn on the ATM. + +//stolen wholesale and then edited a bit from newscasters, which are awesome and by Agouri +/obj/machinery/atm/proc/scan_user(mob/living/carbon/human/human_user as mob) + if(!authenticated_account && linked_db) + if(human_user.wear_id) + var/obj/item/weapon/card/id/I + if(istype(human_user.wear_id, /obj/item/weapon/card/id) ) + I = human_user.wear_id + else if(istype(human_user.wear_id, /obj/item/device/pda) ) + var/obj/item/device/pda/P = human_user.wear_id + I = P.id + if(I) + authenticated_account = attempt_account_access(I.associated_account_number) + if(authenticated_account) + human_user << "\blue \icon[src] Access granted. Welcome user '[authenticated_account.owner_name].'" + + //create a transaction log entry + var/datum/transaction/T = new() + T.target_name = authenticated_account.owner_name + T.purpose = "Remote terminal access" + T.source_terminal = machine_id + T.date = current_date_string + T.time = worldtime2text() + authenticated_account.transaction_log.Add(T) diff --git a/code/modules/economy/POS.dm b/code/modules/economy/POS.dm index bd2e7248f73..8cc707f3b2f 100644 --- a/code/modules/economy/POS.dm +++ b/code/modules/economy/POS.dm @@ -1,538 +1,538 @@ -/************************ -* Point Of Sale -* -* Takes cash or credit. -*************************/ - -/line_item - parent_type = /datum - - var/name = "" - var/price = 0 // Per unit - var/units = 0 - -var/global/current_pos_id = 1 -var/global/pos_sales = 0 - -var/const/RECEIPT_HEADER = {" - - - - -"} -var/const/POS_HEADER = {" - - - - -"} - -#define POS_TAX_RATE 0.10 // 10% - -#define POS_SCREEN_LOGIN 0 -#define POS_SCREEN_ORDER 1 -#define POS_SCREEN_FINALIZE 2 -#define POS_SCREEN_PRODUCTS 3 -#define POS_SCREEN_IMPORT 4 -#define POS_SCREEN_EXPORT 5 -#define POS_SCREEN_SETTINGS 6 -/obj/machinery/pos - icon = 'icons/obj/machines/pos.dmi' - icon_state = "pos" - density = 0 - name = "point of sale" - desc = "Also known as a cash register, or, more commonly, \"robbery magnet\"." - - var/id = 0 - var/sales = 0 - var/department - var/mob/logged_in - var/datum/money_account/linked_account - - var/credits_held = 0 - var/credits_needed = 0 - - var/list/products = list() // name = /line_item - var/list/line_items = list() // # = /line_item - - var/screen=POS_SCREEN_LOGIN - -/obj/machinery/pos/New() - ..() - id = current_pos_id++ - if(department) - linked_account = department_accounts[department] - else - linked_account = station_account - update_icon() - -/obj/machinery/pos/proc/AddToOrder(var/name, var/units) - if(!(name in products)) - return 0 - var/line_item/LI = products[name] - var/line_item/LIC = new - LIC.name=LI.name - LIC.price=LI.price - LIC.units=units - line_items.Add(LIC) - -/obj/machinery/pos/proc/RemoveFromOrder(var/order_id) - line_items.Cut(order_id,order_id+1) - -/obj/machinery/pos/proc/NewOrder() - line_items.Cut() - -/obj/machinery/pos/proc/PrintReceipt(var/order_id) - var/receipt = {"[RECEIPT_HEADER]
POINT OF SALE #[id]
- Paying to: [linked_account.owner_name]
- Cashier: [logged_in]
"} - if(myArea) - receipt += myArea.name - receipt += "
" - receipt += {"
-
[worldtime2text()], [current_date_string]
- - - - - - - "} - var/subtotal=0 - for(var/i=1;i<=line_items.len;i++) - var/line_item/LI = line_items[i] - var/linetotal=LI.units*LI.price - receipt += "" - subtotal += linetotal - var/taxes = POS_TAX_RATE*subtotal - receipt += {" - - - - - - "} - receipt += {" - - "} - receipt += "
ItemAmountUnit PriceLine Total
[LI.name][LI.units]$[num2septext(LI.price)]$[num2septext(linetotal)]
SUBTOTAL$[num2septext(subtotal)]
TAXES$[num2septext(taxes)]
TOTAL$[num2septext(taxes+subtotal)] -
" - - var/obj/item/weapon/paper/P = new(loc) - P.name="Receipt #[id]-[++sales]" - P.info=receipt - - P = new(loc) - P.name="Receipt #[id]-[sales] (Cashier Copy)" - P.info=receipt - - -/obj/machinery/pos/proc/LoginScreen() - return "
Please swipe ID to log in.
" - -/obj/machinery/pos/proc/OrderScreen() - var/receipt = {"
- POS Info - POINT OF SALE #[id]
- Paying to: [linked_account.owner_name]
- Cashier: [logged_in]
"} - if(myArea) - receipt += myArea.name - receipt += "
" - receipt += {"
Order Data -
- - - - - - - - - "} - var/subtotal=0 - if(line_items.len>0) - for(var/i=1;i<=line_items.len;i++) - var/line_item/LI = line_items[i] - var/linetotal=LI.units*LI.price - receipt += {" - - - - - - "} - subtotal += linetotal - var/taxes = POS_TAX_RATE*subtotal - var/presets = "(No presets available)" - if(products.len>0) - presets = {"" - receipt += {" - - - - - - - - - - - "} - receipt += {" - - "} - receipt += {"
ItemAmountUnit PriceLine Total...
[LI.name][LI.units]$[num2septext(LI.price)]$[num2septext(linetotal)]×
[presets] units
SUBTOTAL$[num2septext(subtotal)]
TAXES$[num2septext(taxes)]
TOTAL$[num2septext(taxes+subtotal)] -
- - -
-
"} - return receipt - -/obj/machinery/pos/proc/ProductsScreen() - var/dat={"
Product List -
- - - - - - - - "} - for(var/i in products) - var/line_item/LI = products[i] - dat += {" - - - - - "} - dat += {"
ItemUnit Price# Sold...
[LI.name]$[num2septext(LI.price)][LI.units]×
- New Product:
-
- $
-
- Import | Export -
-
"} - return dat - -/obj/machinery/pos/proc/ExportScreen() - var/dat={"
Export Products as CSV - - OK -
"} - return dat - -/obj/machinery/pos/proc/ImportScreen() - var/dat={"
- Import Products as CSV -
- - -

Data must be in the form of a CSV, with no headers or quotation marks.

-

First column must be product names, second must be prices as an unformatted number (####.##)

-

Deviations from this format will result in your import being rejected.

- -
-
"} - return dat - -/obj/machinery/pos/proc/FinalizeScreen() - return "
Waiting for Credit
Cancel
" - -/obj/machinery/pos/proc/SettingsScreen() - var/dat={"
- -
- Account Settings -
- Payable Account: -
-
-
- Locality Settings -
- Tax Rate: % (LOCKED) -
-
- -
"} - return dat - -/obj/machinery/pos/update_icon() - overlays = 0 - if(stat & (NOPOWER|BROKEN)) return - if(logged_in) - overlays += "pos-working" - else - overlays += "pos-standby" - -/obj/machinery/pos/attack_hand(var/mob/user) - user.set_machine(src) - var/logindata="" - if(logged_in) - logindata={"[logged_in.name]"} - var/dat = POS_HEADER + {" - "} - switch(screen) - if(POS_SCREEN_LOGIN) dat += LoginScreen() - if(POS_SCREEN_ORDER) dat += OrderScreen() - if(POS_SCREEN_FINALIZE) dat += FinalizeScreen() - if(POS_SCREEN_PRODUCTS) dat += ProductsScreen() - if(POS_SCREEN_EXPORT) dat += ExportScreen() - if(POS_SCREEN_IMPORT) dat += ImportScreen() - if(POS_SCREEN_SETTINGS) dat += SettingsScreen() - - dat += "" - // END AUTOFIX - user << browse(dat, "window=pos") - onclose(user, "pos") - return - -/obj/machinery/pos/proc/say(var/text) - src.visible_message("\icon[src] [name] states, \"[text]\"") - -/obj/machinery/pos/Topic(var/href, var/list/href_list) - if(..(href,href_list)) return - if("logout" in href_list) - if(alert(src, "You sure you want to log out?", "Confirm", "Yes", "No")!="Yes") return - logged_in=null - screen=POS_SCREEN_LOGIN - update_icon() - src.attack_hand(usr) - return - if(usr != logged_in) - usr << "\red [logged_in.name] is already logged in. You cannot use this machine until they log out." - return - if("act" in href_list) - switch(href_list["act"]) - if("Reset") - NewOrder() - screen=POS_SCREEN_ORDER - if("Finalize Sale") - var/subtotal=0 - if(line_items.len>0) - for(var/i=1;i<=line_items.len;i++) - var/line_item/LI = line_items[i] - subtotal += LI.units*LI.price - var/taxes = POS_TAX_RATE*subtotal - credits_needed=taxes+subtotal - say("Your total is $[num2septext(credits_needed)]. Please insert credit chips or swipe your ID.") - screen=POS_SCREEN_FINALIZE - if("Add Product") - var/line_item/LI = new - LI.name=sanitize(href_list["name"]) - LI.price=text2num(href_list["price"]) - products["[products.len+1]"]=LI - if("Add to Order") - AddToOrder(href_list["preset"],text2num(href_list["units"])) - if("Add Products") - for(var/list/line in text2list(href_list["csv"],"\n")) - var/list/cells = text2list(line,",") - if(cells.len<2) - usr << "\red The CSV must have at least two columns: Product Name, followed by Price (as a number)." - src.attack_hand(usr) - return - var/line_item/LI = new - LI.name=sanitize(cells[1]) - LI.price=text2num(cells[2]) - products["[products.len+1]"]=LI - if("Export Products") - screen=POS_SCREEN_EXPORT - if("Import Products") - screen=POS_SCREEN_IMPORT - if("Save Settings") - var/datum/money_account/new_linked_account = get_money_account(text2num(href_list["payableto"]),z) - if(!new_linked_account) - usr << "\red Unable to link new account." - else - linked_account = new_linked_account - screen=POS_SCREEN_SETTINGS - else if("screen" in href_list) - screen=text2num(href_list["screen"]) - else if("rmproduct" in href_list) - products.Remove(href_list["rmproduct"]) - else if("removefromorder" in href_list) - RemoveFromOrder(text2num(href_list["removefromorder"])) - else if("setunits" in href_list) - var/lid = text2num(href_list["setunits"]) - var/newunits = input(usr,"Enter the units sold.") as num - if(!newunits) return - var/line_item/LI = line_items[lid] - LI.units = newunits - line_items[lid]=LI - else if("setpname" in href_list) - var/newtext = sanitize(input(usr,"Enter the product's name.")) - if(!newtext) return - var/pid = href_list["setpname"] - var/line_item/LI = products[pid] - LI.name = newtext - products[pid]=LI - else if("setprice" in href_list) - var/newprice = input(usr,"Enter the product's price.") as num - if(!newprice) return - var/pid = href_list["setprice"] - var/line_item/LI = products[pid] - LI.price = newprice - products[pid]=LI - src.attack_hand(usr) - -/obj/machinery/pos/attackby(var/atom/movable/A, var/mob/user, params) - if(istype(A,/obj/item/weapon/card/id)) - var/obj/item/weapon/card/id/I = A - if(!logged_in) - user.visible_message("\blue The machine beeps, and logs you in","You hear a beep.") - logged_in = user - screen=POS_SCREEN_ORDER - update_icon() - src.attack_hand(user) //why'd you use usr nexis, why - return - else - if(!linked_account) - visible_message("\red The machine buzzes, and flashes \"NO LINKED ACCOUNT\" on the screen.","You hear a buzz.") - flick(src,"pos-error") - return - if(screen!=POS_SCREEN_FINALIZE) - visible_message("\blue The machine buzzes.","\red You hear a buzz.") - flick(src,"pos-error") - return - var/datum/money_account/acct = get_card_account(I) - if(!acct) - visible_message("\red The machine buzzes, and flashes \"NO ACCOUNT\" on the screen.","You hear a buzz.") - flick(src,"pos-error") - return - if(credits_needed > acct.money) - visible_message("\red The machine buzzes, and flashes \"NOT ENOUGH FUNDS\" on the screen.","You hear a buzz.") - flick(src,"pos-error") - return - visible_message("\blue The machine beeps, and begins printing a receipt","You hear a beep.") - PrintReceipt() - NewOrder() - acct.charge(credits_needed,linked_account,"Purchase at POS #[id].") - credits_needed=0 - screen=POS_SCREEN_ORDER - else if(istype(A,/obj/item/weapon/spacecash)) - if(!linked_account) - visible_message("\red The machine buzzes, and flashes \"NO LINKED ACCOUNT\" on the screen.","You hear a buzz.") - flick(src,"pos-error") - return - if(!logged_in || screen!=POS_SCREEN_FINALIZE) - visible_message("\blue The machine buzzes.","\red You hear a buzz.") - flick(src,"pos-error") - return - var/obj/item/weapon/spacecash/C=A - credits_held += C.get_total() - if(credits_held >= credits_needed) - visible_message("\blue The machine beeps, and begins printing a receipt","You hear a beep and the sound of paper being shredded.") - PrintReceipt() - NewOrder() - credits_held -= credits_needed - credits_needed=0 - screen=POS_SCREEN_ORDER - if(credits_held) - var/obj/item/weapon/storage/box/B = new(loc) - dispense_cash(credits_held,B) - B.name="change" - B.desc="A box of change." - credits_held=0 - ..() +/************************ +* Point Of Sale +* +* Takes cash or credit. +*************************/ + +/line_item + parent_type = /datum + + var/name = "" + var/price = 0 // Per unit + var/units = 0 + +var/global/current_pos_id = 1 +var/global/pos_sales = 0 + +var/const/RECEIPT_HEADER = {" + + + + +"} +var/const/POS_HEADER = {" + + + + +"} + +#define POS_TAX_RATE 0.10 // 10% + +#define POS_SCREEN_LOGIN 0 +#define POS_SCREEN_ORDER 1 +#define POS_SCREEN_FINALIZE 2 +#define POS_SCREEN_PRODUCTS 3 +#define POS_SCREEN_IMPORT 4 +#define POS_SCREEN_EXPORT 5 +#define POS_SCREEN_SETTINGS 6 +/obj/machinery/pos + icon = 'icons/obj/machines/pos.dmi' + icon_state = "pos" + density = 0 + name = "point of sale" + desc = "Also known as a cash register, or, more commonly, \"robbery magnet\"." + + var/id = 0 + var/sales = 0 + var/department + var/mob/logged_in + var/datum/money_account/linked_account + + var/credits_held = 0 + var/credits_needed = 0 + + var/list/products = list() // name = /line_item + var/list/line_items = list() // # = /line_item + + var/screen=POS_SCREEN_LOGIN + +/obj/machinery/pos/New() + ..() + id = current_pos_id++ + if(department) + linked_account = department_accounts[department] + else + linked_account = station_account + update_icon() + +/obj/machinery/pos/proc/AddToOrder(var/name, var/units) + if(!(name in products)) + return 0 + var/line_item/LI = products[name] + var/line_item/LIC = new + LIC.name=LI.name + LIC.price=LI.price + LIC.units=units + line_items.Add(LIC) + +/obj/machinery/pos/proc/RemoveFromOrder(var/order_id) + line_items.Cut(order_id,order_id+1) + +/obj/machinery/pos/proc/NewOrder() + line_items.Cut() + +/obj/machinery/pos/proc/PrintReceipt(var/order_id) + var/receipt = {"[RECEIPT_HEADER]
POINT OF SALE #[id]
+ Paying to: [linked_account.owner_name]
+ Cashier: [logged_in]
"} + if(myArea) + receipt += myArea.name + receipt += "
" + receipt += {"
+
[worldtime2text()], [current_date_string]
+ + + + + + + "} + var/subtotal=0 + for(var/i=1;i<=line_items.len;i++) + var/line_item/LI = line_items[i] + var/linetotal=LI.units*LI.price + receipt += "" + subtotal += linetotal + var/taxes = POS_TAX_RATE*subtotal + receipt += {" + + + + + + "} + receipt += {" + + "} + receipt += "
ItemAmountUnit PriceLine Total
[LI.name][LI.units]$[num2septext(LI.price)]$[num2septext(linetotal)]
SUBTOTAL$[num2septext(subtotal)]
TAXES$[num2septext(taxes)]
TOTAL$[num2septext(taxes+subtotal)] +
" + + var/obj/item/weapon/paper/P = new(loc) + P.name="Receipt #[id]-[++sales]" + P.info=receipt + + P = new(loc) + P.name="Receipt #[id]-[sales] (Cashier Copy)" + P.info=receipt + + +/obj/machinery/pos/proc/LoginScreen() + return "
Please swipe ID to log in.
" + +/obj/machinery/pos/proc/OrderScreen() + var/receipt = {"
+ POS Info + POINT OF SALE #[id]
+ Paying to: [linked_account.owner_name]
+ Cashier: [logged_in]
"} + if(myArea) + receipt += myArea.name + receipt += "
" + receipt += {"
Order Data +
+ + + + + + + + + "} + var/subtotal=0 + if(line_items.len>0) + for(var/i=1;i<=line_items.len;i++) + var/line_item/LI = line_items[i] + var/linetotal=LI.units*LI.price + receipt += {" + + + + + + "} + subtotal += linetotal + var/taxes = POS_TAX_RATE*subtotal + var/presets = "(No presets available)" + if(products.len>0) + presets = {"" + receipt += {" + + + + + + + + + + + "} + receipt += {" + + "} + receipt += {"
ItemAmountUnit PriceLine Total...
[LI.name][LI.units]$[num2septext(LI.price)]$[num2septext(linetotal)]×
[presets] units
SUBTOTAL$[num2septext(subtotal)]
TAXES$[num2septext(taxes)]
TOTAL$[num2septext(taxes+subtotal)] +
+ + +
+
"} + return receipt + +/obj/machinery/pos/proc/ProductsScreen() + var/dat={"
Product List +
+ + + + + + + + "} + for(var/i in products) + var/line_item/LI = products[i] + dat += {" + + + + + "} + dat += {"
ItemUnit Price# Sold...
[LI.name]$[num2septext(LI.price)][LI.units]×
+ New Product:
+
+ $
+
+ Import | Export +
+
"} + return dat + +/obj/machinery/pos/proc/ExportScreen() + var/dat={"
Export Products as CSV + + OK +
"} + return dat + +/obj/machinery/pos/proc/ImportScreen() + var/dat={"
+ Import Products as CSV +
+ + +

Data must be in the form of a CSV, with no headers or quotation marks.

+

First column must be product names, second must be prices as an unformatted number (####.##)

+

Deviations from this format will result in your import being rejected.

+ +
+
"} + return dat + +/obj/machinery/pos/proc/FinalizeScreen() + return "
Waiting for Credit
Cancel
" + +/obj/machinery/pos/proc/SettingsScreen() + var/dat={"
+ +
+ Account Settings +
+ Payable Account: +
+
+
+ Locality Settings +
+ Tax Rate: % (LOCKED) +
+
+ +
"} + return dat + +/obj/machinery/pos/update_icon() + overlays = 0 + if(stat & (NOPOWER|BROKEN)) return + if(logged_in) + overlays += "pos-working" + else + overlays += "pos-standby" + +/obj/machinery/pos/attack_hand(var/mob/user) + user.set_machine(src) + var/logindata="" + if(logged_in) + logindata={"[logged_in.name]"} + var/dat = POS_HEADER + {" + "} + switch(screen) + if(POS_SCREEN_LOGIN) dat += LoginScreen() + if(POS_SCREEN_ORDER) dat += OrderScreen() + if(POS_SCREEN_FINALIZE) dat += FinalizeScreen() + if(POS_SCREEN_PRODUCTS) dat += ProductsScreen() + if(POS_SCREEN_EXPORT) dat += ExportScreen() + if(POS_SCREEN_IMPORT) dat += ImportScreen() + if(POS_SCREEN_SETTINGS) dat += SettingsScreen() + + dat += "" + // END AUTOFIX + user << browse(dat, "window=pos") + onclose(user, "pos") + return + +/obj/machinery/pos/proc/say(var/text) + src.visible_message("\icon[src] [name] states, \"[text]\"") + +/obj/machinery/pos/Topic(var/href, var/list/href_list) + if(..(href,href_list)) return + if("logout" in href_list) + if(alert(src, "You sure you want to log out?", "Confirm", "Yes", "No")!="Yes") return + logged_in=null + screen=POS_SCREEN_LOGIN + update_icon() + src.attack_hand(usr) + return + if(usr != logged_in) + usr << "\red [logged_in.name] is already logged in. You cannot use this machine until they log out." + return + if("act" in href_list) + switch(href_list["act"]) + if("Reset") + NewOrder() + screen=POS_SCREEN_ORDER + if("Finalize Sale") + var/subtotal=0 + if(line_items.len>0) + for(var/i=1;i<=line_items.len;i++) + var/line_item/LI = line_items[i] + subtotal += LI.units*LI.price + var/taxes = POS_TAX_RATE*subtotal + credits_needed=taxes+subtotal + say("Your total is $[num2septext(credits_needed)]. Please insert credit chips or swipe your ID.") + screen=POS_SCREEN_FINALIZE + if("Add Product") + var/line_item/LI = new + LI.name=sanitize(href_list["name"]) + LI.price=text2num(href_list["price"]) + products["[products.len+1]"]=LI + if("Add to Order") + AddToOrder(href_list["preset"],text2num(href_list["units"])) + if("Add Products") + for(var/list/line in text2list(href_list["csv"],"\n")) + var/list/cells = text2list(line,",") + if(cells.len<2) + usr << "\red The CSV must have at least two columns: Product Name, followed by Price (as a number)." + src.attack_hand(usr) + return + var/line_item/LI = new + LI.name=sanitize(cells[1]) + LI.price=text2num(cells[2]) + products["[products.len+1]"]=LI + if("Export Products") + screen=POS_SCREEN_EXPORT + if("Import Products") + screen=POS_SCREEN_IMPORT + if("Save Settings") + var/datum/money_account/new_linked_account = get_money_account(text2num(href_list["payableto"]),z) + if(!new_linked_account) + usr << "\red Unable to link new account." + else + linked_account = new_linked_account + screen=POS_SCREEN_SETTINGS + else if("screen" in href_list) + screen=text2num(href_list["screen"]) + else if("rmproduct" in href_list) + products.Remove(href_list["rmproduct"]) + else if("removefromorder" in href_list) + RemoveFromOrder(text2num(href_list["removefromorder"])) + else if("setunits" in href_list) + var/lid = text2num(href_list["setunits"]) + var/newunits = input(usr,"Enter the units sold.") as num + if(!newunits) return + var/line_item/LI = line_items[lid] + LI.units = newunits + line_items[lid]=LI + else if("setpname" in href_list) + var/newtext = sanitize(input(usr,"Enter the product's name.")) + if(!newtext) return + var/pid = href_list["setpname"] + var/line_item/LI = products[pid] + LI.name = newtext + products[pid]=LI + else if("setprice" in href_list) + var/newprice = input(usr,"Enter the product's price.") as num + if(!newprice) return + var/pid = href_list["setprice"] + var/line_item/LI = products[pid] + LI.price = newprice + products[pid]=LI + src.attack_hand(usr) + +/obj/machinery/pos/attackby(var/atom/movable/A, var/mob/user, params) + if(istype(A,/obj/item/weapon/card/id)) + var/obj/item/weapon/card/id/I = A + if(!logged_in) + user.visible_message("\blue The machine beeps, and logs you in","You hear a beep.") + logged_in = user + screen=POS_SCREEN_ORDER + update_icon() + src.attack_hand(user) //why'd you use usr nexis, why + return + else + if(!linked_account) + visible_message("\red The machine buzzes, and flashes \"NO LINKED ACCOUNT\" on the screen.","You hear a buzz.") + flick(src,"pos-error") + return + if(screen!=POS_SCREEN_FINALIZE) + visible_message("\blue The machine buzzes.","\red You hear a buzz.") + flick(src,"pos-error") + return + var/datum/money_account/acct = get_card_account(I) + if(!acct) + visible_message("\red The machine buzzes, and flashes \"NO ACCOUNT\" on the screen.","You hear a buzz.") + flick(src,"pos-error") + return + if(credits_needed > acct.money) + visible_message("\red The machine buzzes, and flashes \"NOT ENOUGH FUNDS\" on the screen.","You hear a buzz.") + flick(src,"pos-error") + return + visible_message("\blue The machine beeps, and begins printing a receipt","You hear a beep.") + PrintReceipt() + NewOrder() + acct.charge(credits_needed,linked_account,"Purchase at POS #[id].") + credits_needed=0 + screen=POS_SCREEN_ORDER + else if(istype(A,/obj/item/weapon/spacecash)) + if(!linked_account) + visible_message("\red The machine buzzes, and flashes \"NO LINKED ACCOUNT\" on the screen.","You hear a buzz.") + flick(src,"pos-error") + return + if(!logged_in || screen!=POS_SCREEN_FINALIZE) + visible_message("\blue The machine buzzes.","\red You hear a buzz.") + flick(src,"pos-error") + return + var/obj/item/weapon/spacecash/C=A + credits_held += C.get_total() + if(credits_held >= credits_needed) + visible_message("\blue The machine beeps, and begins printing a receipt","You hear a beep and the sound of paper being shredded.") + PrintReceipt() + NewOrder() + credits_held -= credits_needed + credits_needed=0 + screen=POS_SCREEN_ORDER + if(credits_held) + var/obj/item/weapon/storage/box/B = new(loc) + dispense_cash(credits_held,B) + B.name="change" + B.desc="A box of change." + credits_held=0 + ..() diff --git a/code/modules/economy/cash.dm b/code/modules/economy/cash.dm index c90ee6aa62e..22bbef5d805 100644 --- a/code/modules/economy/cash.dm +++ b/code/modules/economy/cash.dm @@ -1,50 +1,50 @@ -/obj/item/weapon/spacecash - name = "0 credit chip" - desc = "It's worth 0 credits." - gender = PLURAL - icon = 'icons/obj/items.dmi' - icon_state = "spacecash" - opacity = 0 - density = 0 - anchored = 0.0 - force = 1.0 - throwforce = 1.0 - throw_speed = 1 - throw_range = 2 - w_class = 1.0 - var/access = list() - access = access_crate_cash - var/worth = 0 - -/obj/item/weapon/spacecash/c1 - icon_state = "spacecash" - worth = 1 - -/obj/item/weapon/spacecash/c10 - icon_state = "spacecash10" - worth = 10 - -/obj/item/weapon/spacecash/c20 - icon_state = "spacecash20" - worth = 20 - -/obj/item/weapon/spacecash/c50 - icon_state = "spacecash50" - worth = 50 - -/obj/item/weapon/spacecash/c100 - icon_state = "spacecash100" - worth = 100 - -/obj/item/weapon/spacecash/c200 - icon_state = "spacecash200" - worth = 200 - -/obj/item/weapon/spacecash/c500 - icon_state = "spacecash500" - worth = 500 - -/obj/item/weapon/spacecash/c1000 - icon_state = "spacecash1000" - worth = 1000 - +/obj/item/weapon/spacecash + name = "0 credit chip" + desc = "It's worth 0 credits." + gender = PLURAL + icon = 'icons/obj/items.dmi' + icon_state = "spacecash" + opacity = 0 + density = 0 + anchored = 0.0 + force = 1.0 + throwforce = 1.0 + throw_speed = 1 + throw_range = 2 + w_class = 1.0 + var/access = list() + access = access_crate_cash + var/worth = 0 + +/obj/item/weapon/spacecash/c1 + icon_state = "spacecash" + worth = 1 + +/obj/item/weapon/spacecash/c10 + icon_state = "spacecash10" + worth = 10 + +/obj/item/weapon/spacecash/c20 + icon_state = "spacecash20" + worth = 20 + +/obj/item/weapon/spacecash/c50 + icon_state = "spacecash50" + worth = 50 + +/obj/item/weapon/spacecash/c100 + icon_state = "spacecash100" + worth = 100 + +/obj/item/weapon/spacecash/c200 + icon_state = "spacecash200" + worth = 200 + +/obj/item/weapon/spacecash/c500 + icon_state = "spacecash500" + worth = 500 + +/obj/item/weapon/spacecash/c1000 + icon_state = "spacecash1000" + worth = 1000 + diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 4307c6cbd0b..fc2bc6e4c0e 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -84,6 +84,9 @@ if(stat == DEAD) return if(healths) healths.icon_state = "health5" + if(!gibbed) + emote("deathgasp") //let the world KNOW WE ARE DEAD + stat = DEAD dizziness = 0 jitteriness = 0 @@ -128,8 +131,6 @@ H.mind.kills += "[name] ([ckey])" if(!gibbed) - emote("deathgasp") //let the world KNOW WE ARE DEAD - update_canmove() if(client) blind.layer = 0 @@ -175,4 +176,4 @@ /mob/living/carbon/human/proc/Drain() ChangeToHusk() mutations |= NOCLONE - return + return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 414335a243e..6bc82f363d7 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -1,4 +1,8 @@ /mob/living/carbon/human/emote(var/act,var/m_type=1,var/message = null,var/force) + + if (stat == DEAD) + return // No screaming bodies + var/param = null if (findtext(act, "-", 1, null)) var/t1 = findtext(act, "-", 1, null) @@ -814,4 +818,4 @@ set desc = "Sets an extended description of your character's features." set category = "IC" - flavor_text = TextPreview(input(usr, "Please enter your new flavour text.", "Flavour text", null) as text) + flavor_text = TextPreview(input(usr, "Please enter your new flavour text.", "Flavour text", null) as text) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index eb53f62a774..c6513be0799 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -73,6 +73,7 @@ var/mob/remoteview_target = null var/meatleft = 3 //For chef item var/decaylevel = 0 // For rotting bodies + var/max_blood = 560 // For stuff in the vessel var/slime_color = "blue" //For slime people this defines their color, it's blue by default to pay tribute to the old icons var/check_mutations=0 // Check mutations on next life tick diff --git a/code/modules/reagents/Chemistry-Readme.dm b/code/modules/reagents/Chemistry-Readme.dm index 77921d0ab07..61e2a0c82a0 100644 --- a/code/modules/reagents/Chemistry-Readme.dm +++ b/code/modules/reagents/Chemistry-Readme.dm @@ -98,6 +98,9 @@ About the Holder: Returns the amount of the matching reagent inside the holder. Returns 0 if the reagent is missing. + overdose_list() + Returns a list of all the chemical IDs in the reagent holder that are overdosing + Important variables: total_volume diff --git a/code/modules/reagents/newchem/newchem_procs.dm b/code/modules/reagents/newchem/newchem_procs.dm index b2d6edc0995..faf530f6b3e 100644 --- a/code/modules/reagents/newchem/newchem_procs.dm +++ b/code/modules/reagents/newchem/newchem_procs.dm @@ -82,6 +82,14 @@ datum/reagents/proc/metabolize(var/mob/M) addiction_tick++ update_total() +/datum/reagents/proc/overdose_list() + var/od_chems[0] + for(var/datum/reagent/R in reagent_list) + if(R.overdosed) + od_chems.Add(R.id) + return od_chems + + datum/reagents/proc/reagent_on_tick() for(var/datum/reagent/R in reagent_list) R.on_tick() diff --git a/nano/css/shared.css b/nano/css/shared.css index 786b577e615..b9ceac02814 100644 --- a/nano/css/shared.css +++ b/nano/css/shared.css @@ -606,4 +606,29 @@ th.misc { .Pearl { color: #C6CACB; +} + +/* Status bar colors for sleeper temperatures */ +.cold1 { + color: #94B8FF; +} + +.cold2 { + color: #6699FF; +} + +.cold3 { + color: #293D66; +} + +.hot1 { + color: #FF9966; +} + +.hot2 { + color: #993D00; +} + +.hot3 { + color: #FF6600; } \ No newline at end of file diff --git a/nano/templates/pda.tmpl b/nano/templates/pda.tmpl index 753e06f299e..4f74a358930 100644 --- a/nano/templates/pda.tmpl +++ b/nano/templates/pda.tmpl @@ -69,6 +69,14 @@ Used In File(s): \code\game\objects\items\devices\PDA\PDA.dm margin: 0 2px 2px 0; } + .uiIcon16 { + background-image: none; + float: left; + width: auto; + height: auto; + margin: 0 0 0 0; + } + #uiTitleText { color: #000000; } @@ -307,7 +315,7 @@ Used In File(s): \code\game\objects\items\devices\PDA\PDA.dm

Current Conversations

{{for data.convopdas}}
- {{:helper.link(value.Name, 'circle-arrow-s', {'choice' : "Select Conversation", 'convo' : value.Reference } , null, 'pdalink fixedLeftWider')}} + {{:helper.link(value.Name, 'circle-arrow-s', {'choice' : "Select Conversation", 'convo' : value.Reference } , null, 'pdalink')}} {{if data.cartridge}} {{if data.cartridge.access.access_detonate_pda && value.Detonate}} {{:helper.link('*Detonate*', 'radiation', {'choice' : "Detonate", 'target' : value.Reference}, null, 'pdalink fixedLeft')}} @@ -324,7 +332,7 @@ Used In File(s): \code\game\objects\items\devices\PDA\PDA.dm

Other PDAs

{{for data.pdas}}
- {{:helper.link(value.Name, 'circle-arrow-s', {'choice' : "Message", 'target' : value.Reference}, null, 'pdalink fixedLeftWider')}} + {{:helper.link(value.Name, 'circle-arrow-s', {'choice' : "Message", 'target' : value.Reference}, null, 'pdalink')}} {{if data.cartridge}} {{if data.cartridge.access.access_detonate_pda && value.Detonate}} {{:helper.link('*Detonate*', 'radiation', {'choice' : "Detonate", 'target' : value.Reference}, null, 'pdalink fixedLeft')}} {{/if}} {{if data.cartridge.access.access_clown}} {{:helper.link('*Send Virus*', 'star', {'choice' : "Send Honk", 'target' : value.Reference}, null, 'pdalink fixedLeft')}} {{/if}} diff --git a/nano/templates/sleeper.tmpl b/nano/templates/sleeper.tmpl new file mode 100644 index 00000000000..8cd59a4e4b9 --- /dev/null +++ b/nano/templates/sleeper.tmpl @@ -0,0 +1,201 @@ + +

Sleeper Status

+ +
+ {{if !data.hasOccupant}} +
Sleeper Unoccupied
+ {{else}} +
+ {{:data.occupant.name}} =>  + {{if data.occupant.stat == 0}} + Conscious + {{else data.occupant.stat == 1}} + Unconscious + {{else}} + DEAD + {{/if}} +
+ +
+
Health:
+ {{if data.occupant.health >= data.occupant.maxHealth}} + {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}} + {{else data.occupant.health > 0}} + {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'average')}} + {{else data.occupant.health >= data.minhealth}} + {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}} + {{else}} + {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'bad alignRight')}} + {{/if}} +
{{:helper.round(data.occupant.health)}}
+
+ +
+
=> Brute Damage:
+ {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}} +
{{:helper.round(data.occupant.bruteLoss)}}
+
+ +
+
=> Resp. Damage:
+ {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}} +
{{:helper.round(data.occupant.oxyLoss)}}
+
+ +
+
=> Toxin Damage:
+ {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}} +
{{:helper.round(data.occupant.toxLoss)}}
+
+ +
+
=> Burn Severity:
+ {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}} +
{{:helper.round(data.occupant.fireLoss)}}
+
+ +
+
+ +
Patient Temperature:
+ {{if data.occupant.temperatureSuitability == -3}} + {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} +
{{:helper.round(data.occupant.btCelsius)}}°C, {{:helper.round(data.occupant.btFaren)}}°F
+ {{else data.occupant.temperatureSuitability == -2}} + {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} +
{{:helper.round(data.occupant.btCelsius)}}°C, {{:helper.round(data.occupant.btFaren)}}°F
+ {{else data.occupant.temperatureSuitability == -1}} + {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} +
{{:helper.round(data.occupant.btCelsius)}}°C, {{:helper.round(data.occupant.btFaren)}}°F
+ {{else data.occupant.temperatureSuitability == 0}} + {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}} +
{{:helper.round(data.occupant.btCelsius)}}°C, {{:helper.round(data.occupant.btFaren)}}°F
+ {{else data.occupant.temperatureSuitability == 1}} + {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} +
{{:helper.round(data.occupant.btCelsius)}}°C, {{:helper.round(data.occupant.btFaren)}}°F
+ {{else data.occupant.temperatureSuitability == 2}} + {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} +
{{:helper.round(data.occupant.btCelsius)}}°C, {{:helper.round(data.occupant.btFaren)}}°F
+ {{else data.occupant.temperatureSuitability == 3}} + {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} +
{{:helper.round(data.occupant.btCelsius)}}°C, {{:helper.round(data.occupant.btFaren)}}°F
+ {{/if}} +
+ + + {{if data.occupant.hasBlood}} + +
+
+
Pulse:
{{:data.occupant.pulse}} bpm
+
+
+
Blood Level:
+ {{if data.occupant.bloodPercent <= 60}} + {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}} +
+ {{:data.occupant.bloodPercent}}%, {{:data.occupant.bloodLevel}}cl +
+ {{else data.occupant.bloodPercent <= 90}} + {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}} +
+ {{:data.occupant.bloodPercent}}%, {{:data.occupant.bloodLevel}}cl +
+ {{else}} + {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}} +
+ {{:data.occupant.bloodPercent}}%, {{:data.occupant.bloodLevel}}cl +
+ {{/if}} +
+ {{/if}} + {{/if}} +
+ +

Sleeper Operation

+
+
+ Sleeper Status: +
+
+ {{:helper.link('Eject Occupant', 'arrowreturnthick-1-s', {'ejectify' : 1}, data.hasOccupant ? null : 'disabled')}} +
+
+
 
+
+
+ Dialysis Beaker: +
+
+ {{if data.isBeakerLoaded}} + {{:helper.round(data.beakerFreeSpace)}} units of space remaining
+ {{else}} + No Dialysis Output Beaker Loaded + {{/if}} +
+
+ {{:helper.link('Eject Beaker', 'eject', {'removebeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}} + {{if data.isBeakerLoaded}} +
+
+ {{:helper.link('On', 'power', {'togglefilter' : 1}, data.occupant.hasBlood ? (data.dialysis ? 'selected' : null) : 'disabled')}}{{:helper.link('Off', 'close', {'togglefilter' : 1}, data.dialysis ? null : 'selected')}} +
+
+ {{/if}} +
+
+
+
+ Chemicals: +
+ + {{for data.chemicals}} +
+ {{:value.title}}: + {{if value.overdosing}} + {{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'bad')}} + {{else value.od_warning}} + {{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'average')}} + {{else}} + {{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'good')}} + {{/if}} +
{{:value.pretty_amount}}/{{:data.maxchem}}
+
+
+
+ {{:helper.link('5', 'gear', {'chemical' : value.id, 'amount' : 5}, (!value.injectable || ((value.occ_amount + 5) > data.maxchem)) ? 'disabled' : null)}} + {{:helper.link('10', 'gear', {'chemical' : value.id, 'amount' : 10}, (!value.injectable || ((value.occ_amount + 10) > data.maxchem)) ? 'disabled' : null)}} +
+
+ {{/for}} +
+