Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Makes wayfinding great again #56055

Merged
merged 23 commits into from Jan 16, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 22 additions & 0 deletions code/game/atoms.dm
Expand Up @@ -1769,3 +1769,25 @@
/atom/proc/InitializeAIController()
if(ai_controller)
ai_controller = new ai_controller(src)

/**
* Point at an atom
*
* Intended to enable and standardise the pointing animation for all atoms
*
* Not intended as a replacement for the mob verb
*/
/atom/proc/pointat(atom/A)
cacogen marked this conversation as resolved.
Show resolved Hide resolved
if(!src || !isturf(src.loc))
cacogen marked this conversation as resolved.
Show resolved Hide resolved
return FALSE

var/turf/tile = get_turf(A)
if (!tile)
return FALSE

var/turf/our_tile = get_turf(src)
var/obj/visual = new /obj/effect/temp_visual/point(our_tile, invisibility)

animate(visual, pixel_x = (tile.x - our_tile.x) * world.icon_size + A.pixel_x, pixel_y = (tile.y - our_tile.y) * world.icon_size + A.pixel_y, time = 1.7, easing = EASE_OUT)

return TRUE
196 changes: 142 additions & 54 deletions code/game/objects/items/wayfinding.dm
Expand Up @@ -2,18 +2,45 @@
name = "wayfinding pinpointer synthesizer"
icon = 'icons/obj/machines/wayfinding.dmi'
icon_state = "pinpointersynth"
desc = "Having trouble finding your way? This machine synthesizes pinpointers that point to common locations."
desc = "A machine given the thankless job of trying to sell wayfinding pinpointers. They point to common locations."
density = FALSE
layer = HIGH_OBJ_LAYER
armor = list(MELEE = 80, BULLET = 30, LASER = 30, ENERGY = 60, BOMB = 90, BIO = 0, RAD = 0, FIRE = 100, ACID = 80)
///List of user-specific cooldowns to prevent pinpointer spam.
var/list/user_spawn_cooldowns = list()
///List of user-specific cooldowns to prevent message spam.
var/list/user_interact_cooldowns = list()
var/spawn_cooldown = 5 MINUTES //time per person to spawn another pinpointer
var/interact_cooldown = 20 SECONDS //time per person for subsequent interactions
var/start_bal = 200 //how much money it starts with to cover wayfinder refunds
var/refund_amt = 40 //how much money recycling a pinpointer rewards you
///Time per person to spawn another pinpointer.
var/spawn_cooldown = 3 MINUTES
///Time per person for subsequent interactions.
var/interact_cooldown = 6 SECONDS
///How many credits the dispenser account starts with to cover wayfinder refunds.
var/start_bal = 400
///How many credits recycling a pinpointer rewards you.
var/refund_amt = 40
cacogen marked this conversation as resolved.
Show resolved Hide resolved
var/datum/bank_account/synth_acc = new /datum/bank_account/remote
var/ppt_cost = 65 //Jan 6 '20: Assistant can buy one roundstart (125 cr starting)
var/ppt_cost = 0 //Jan 9 '21: 2560 had its difficulties for NT as well
var/expression_timer
///Avoid being Reddit.
var/funnyprob = 1
///List of slogans used by the dispenser to attract customers.
var/list/slogan_list = list("Find a wayfinding pinpointer? Give it to me. I'll make it worth your while. Please. Daddy needs his medicine.", //the last sentence is a reference to Sealab 2021
"See a wayfinding pinpointer? Don't let it go to the crusher! Feed it to me instead! Please. I'll pay you.", //I see these things heading for disposals through cargo all the time
"Bleeding to death? Can't read? Find your way to medbay today!", //there are signs that point to medbay but you need basic literacy to get the most out of them
"Voted tenth best pinpointer in the universe in 2560!", //there were no more than ten pinpointers in the game in 2020
"Helping assistants find the departments they tide since 2560.", //not really but it's advertising
"These pinpointers are flying out the airlock!", //because they're being thrown into space
"Grey pinpointers for the grey tide!", //I didn't pick the colour but it works
"Feeling lost? Find direction in life with a wayfinding pinpointer.",
"Automate your sense of direction. Buy a wayfinding pinpointer today!",
"Feed me a stray pinpointer.", //this is an American Psycho reference
"We need a slogan!") //this is a Liberal Crime Squad reference
///Number of last entry we said in our slogan list.
var/previous_slogan = 0
///Last world tick we said a slogan.
var/last_slogan = 0
Rohesie marked this conversation as resolved.
Show resolved Hide resolved
///How many deciseconds until we can say another slogan.
var/slogan_delay = 2 MINUTES

/obj/machinery/pinpointer_dispenser/Initialize(mapload)
. = ..()
Expand All @@ -23,99 +50,160 @@

synth_acc.account_holder = name

desc += " Only [ppt_cost] credits! It also likes making costumes..."
desc += " [ppt_cost ? "Only [ppt_cost] credits! " : ""]It also synthesises costumes for some reason."

set_expression("neutral")
power_change()

last_slogan = world.time + rand(0, slogan_delay)
slogan_list = shuffle(slogan_list) //we can then go through our randomised list without barks repeating

/obj/machinery/pinpointer_dispenser/power_change()
. = ..()
cut_overlays()
if(powered())
set_expression("veryhappy") //actual first law of robotics it needs to be friendly in a mindless way
START_PROCESSING(SSmachines, src)

/obj/machinery/pinpointer_dispenser/process(delta_time)
if(machine_stat & (BROKEN|NOPOWER))
return PROCESS_KILL

if(((last_slogan + slogan_delay) <= world.time) && slogan_list.len)
var/slogan = slogan_list[previous_slogan + 1]
say(slogan)
previous_slogan++
last_slogan = world.time

/obj/machinery/pinpointer_dispenser/attack_hand(mob/living/user)
. = ..()

if(machine_stat & (BROKEN|NOPOWER))
return

if(world.time < user_interact_cooldowns[user.real_name])
to_chat(user, "<span class='warning'>It doesn't respond.</span>")
set_expression("veryhappy", 2 SECONDS)
cacogen marked this conversation as resolved.
Show resolved Hide resolved
to_chat(user, "<span class='notice'>It just grins at you. Maybe you should give it a few seconds?</span>")
return

user_interact_cooldowns[user.real_name] = world.time + interact_cooldown

for(var/obj/item/pinpointer/wayfinding/WP in user.GetAllContents())
set_expression("unsure", 2 SECONDS)
say("<span class='robot'>I can detect the pinpointer on you, [user.first_name()].</span>")
user_spawn_cooldowns[user.real_name] = world.time + spawn_cooldown //spawn timer resets for trickers
set_expression("veryhappy", 2 SECONDS)
say("<span class='robot'>You already have a pinpointer!</span>")
return

var/msg
var/dispense = TRUE
var/obj/item/pinpointer/wayfinding/pointat
for(var/obj/item/pinpointer/wayfinding/WP in range(7, user))
if(WP.Adjacent(user))
set_expression("facepalm", 2 SECONDS)
say("<span class='robot'>[WP.owner == user.real_name ? "Your" : "A"] pinpointer is right there.</span>")
pointat(WP)
user_spawn_cooldowns[user.real_name] = world.time + spawn_cooldown
return
else if(WP in oview(7, user))
pointat = WP
break
var/pnpts_found = 0
for(var/obj/item/pinpointer/wayfinding/WP in view(9, src))
pointat(WP)
pnpts_found++

if(pnpts_found)
set_expression("veryhappy", 2 SECONDS)
say("<span class='robot'>[pnpts_found == 1 ? "There's a pinpointer" : "There are pinpointers"] there!</span>")
return

if(world.time < user_spawn_cooldowns[user.real_name])
var/secsleft = (user_spawn_cooldowns[user.real_name] - world.time) / 10
msg += "to wait another [secsleft/60 > 1 ? "[round(secsleft/60,1)] minute\s" : "[round(secsleft)] second\s"]"
msg += "to wait [secsleft/60 > 1 ? "[round(secsleft/60,1)] more minute\s" : "[round(secsleft)] more second\s"] before I can dispense another pinpointer"
dispense = FALSE

var/datum/bank_account/cust_acc = user.get_bank_account()

if(cust_acc)
if(!cust_acc.has_money(ppt_cost))
if(ppt_cost)
if(!cust_acc)
msg += "a bank account to buy a pinpointer"
dispense = FALSE
else if(!cust_acc.has_money(ppt_cost))
msg += "[!msg ? "to find [ppt_cost-cust_acc.account_balance] more credit\s" : " and find [ppt_cost-cust_acc.account_balance] more credit\s"]"
dispense = FALSE
else if(synth_acc.transfer_money(cust_acc, ppt_cost))
dispense = TRUE

if(!dispense)
set_expression("sad", 2 SECONDS)
if(pointat)
msg += ". I suggest you get [pointat.owner == user.real_name ? "your" : "that"] pinpointer over there instead"
msg += ". Get [pointat.owner == user.real_name ? "your" : "that"] pinpointer over there instead"
pointat(pointat)
say("<span class='robot'>You will need [msg], [user.first_name()].</span>")
return

if(synth_acc.transfer_money(cust_acc, ppt_cost))
say("<span class='robot'>Sorry, [user.first_name()]! You'll need [msg]!</span>")
else
set_expression("veryhappy", 2 SECONDS)
say("<span class='robot'>That is [ppt_cost] credits. Here is your pinpointer.</span>")
say("<span class='robot'>Here's your pinpointer!</span>")
var/obj/item/pinpointer/wayfinding/P = new /obj/item/pinpointer/wayfinding(get_turf(src))
user_spawn_cooldowns[user.real_name] = world.time + spawn_cooldown
user.put_in_hands(P)
P.owner = user.real_name

/obj/machinery/pinpointer_dispenser/attackby(obj/item/I, mob/user, params)
if(machine_stat & (BROKEN|NOPOWER))
return ..()

if(istype(I, /obj/item/pinpointer/wayfinding))
var/obj/item/pinpointer/wayfinding/WP = I

to_chat(user, "<span class='notice'>You put \the [WP] in the return slot.</span>")
var/rfnd_amt
if((!WP.roundstart || WP.owner != user.real_name) && synth_acc.has_money(TRUE)) //can't recycle own pinpointer for money if not bought; given by a neutral quirk
if(synth_acc.has_money(refund_amt))
rfnd_amt = refund_amt
else
rfnd_amt = synth_acc.account_balance
synth_acc._adjust_money(-rfnd_amt)
var/obj/item/holochip/HC = new /obj/item/holochip(user.loc)
HC.credits = rfnd_amt
HC.name = "[HC.credits] credit holochip"
if(istype(user, /mob/living/carbon/human))
var/mob/living/carbon/human/H = user
H.put_in_hands(HC)
else
var/crap = pick(subtypesof(/obj/effect/spawner/bundle/costume)) //harmless garbage some people may appreciate
new crap(user.loc)
qdel(WP)
set_expression("happy", 2 SECONDS)
say("<span class='robot'>Thank you for recycling, [user.first_name()]! Here is [rfnd_amt ? "[rfnd_amt] credits." : "a freshly synthesized costume!"]</span>")

var/refundiscredits = FALSE
var/itsmypinpointer = TRUE

//will they meet the conditions to get a credit reward for recycling?
if(WP.owner != user.real_name)
itsmypinpointer = FALSE

if(synth_acc.has_money(refund_amt) && !WP.roundstart) //is the pinpointer not from the quirk?
refundiscredits = TRUE
qdel(WP)
synth_acc._adjust_money(-refund_amt)
var/obj/item/holochip/HC = new /obj/item/holochip(loc)
HC.credits = refund_amt
HC.name = "[HC.credits] credit holochip"
if(istype(user, /mob/living/carbon/human))
cacogen marked this conversation as resolved.
Show resolved Hide resolved
var/mob/living/carbon/human/H = user
H.put_in_hands(HC)
cacogen marked this conversation as resolved.
Show resolved Hide resolved

if(!refundiscredits)
qdel(WP)
var/costume = pick(subtypesof(/obj/effect/spawner/bundle/costume))
new costume(user.loc)

set_expression("veryhappy", 2 SECONDS)

var/refund = "are [refund_amt] credits."
var/whatyoudid = "recycling"
var/whosepinpointer = "your pinpointer" //to imply they got a costume instead of money because it was their pinpointer they recycled

if(!refundiscredits)
refund = "is a freshly synthesised costume!"
if(prob(funnyprob))
refund = "is a pulse rifle! Just kidding it's a costume."

if(prob(funnyprob))
whatyoudid = "feeding me"

if(!itsmypinpointer)
whosepinpointer = "that pinpointer"

say("<span class='robot'>Thank you for [whatyoudid] [whosepinpointer], [user.first_name()]! Here [refund]</span>")

else
..()

/obj/machinery/pinpointer_dispenser/proc/set_expression(type, duration)
cut_overlays()

if(machine_stat & (BROKEN|NOPOWER))
return

deltimer(expression_timer)
add_overlay(type)
if(duration)
expression_timer = addtimer(CALLBACK(src, .proc/set_expression, "neutral"), duration, TIMER_STOPPABLE)
expression_timer = addtimer(CALLBACK(src, .proc/set_expression, "happy"), duration, TIMER_STOPPABLE)

/obj/machinery/pinpointer_dispenser/proc/pointat(atom)
visible_message("<span class='name'>[src]</span> points at [atom].")
new /obj/effect/temp_visual/point(atom,invisibility)
/obj/machinery/pinpointer_dispenser/pointat(A)
. = ..()
visible_message("<span class='name'>[src]</span> points at [A]. [prob(funnyprob) ? "How'd it do that?" : ""]")

//Pinpointer itself
/obj/item/pinpointer/wayfinding //Help players new to a station find their way around
Expand Down
10 changes: 1 addition & 9 deletions code/modules/mob/mob.dm
Expand Up @@ -571,20 +571,12 @@
set name = "Point To"
set category = "Object"

if(!src || !isturf(src.loc))
return FALSE
if(client && !(A in view(client.view, src)))
return FALSE
if(istype(A, /obj/effect/temp_visual/point))
return FALSE

var/turf/tile = get_turf(A)
if (!tile)
return FALSE

var/turf/our_tile = get_turf(src)
var/obj/visual = new /obj/effect/temp_visual/point(our_tile, invisibility)
animate(visual, pixel_x = (tile.x - our_tile.x) * world.icon_size + A.pixel_x, pixel_y = (tile.y - our_tile.y) * world.icon_size + A.pixel_y, time = 1.7, easing = EASE_OUT)
pointat(A)

return TRUE

Expand Down