ID:152594
 
There are so many ways to abuse the interface in BYOND games that it just aggrivates me. For example, I've got a little system where if you're next to a blacksmith, a statpanel will appear displaying any equipment he's willing to repair. When you double-click the equipment from there, you get a popup quoting you the repair cost and given a choice to accept the offer or not.

So, what you do is get your equipment every-so-slightly damaged, then go to the repair guy and popup a quote box for each piece of equipment you have. Since its relatively undamaged, its relatively cheap to fix. Then you keep all of those boxes open, but move them to the corner of the screen somewhere out of the way. Then you go wear your equipment out until its almost ruined, then you go click "yes" on the boxes in the corner. Poof! All your ruined equipment restored for cheap.

Now I've got to come up with tasteful ways to counteract this kind of abuse, and try to hunt down any possible sources of it. I'm quite envious of games that move you to another screen while you're shopping, or something like that so that you can't have your characters wandering around doing stuff, messing with their inventory and whatnot, while in the process!

Suppose we try the simple approach: Freeze the player while the alert box is up. Double-click, freeze players, display box, when the press okay, un-freeze players. So, player opens one alert box, freezes, opens another alert box, freezes, closes the one alert box, un-freezes, and is able to walk about freely with the other alert box open.
Probably the simplest way to resolve this would be to do something like:

mob/npc/blacksmith/talk(mob/talker) //or whatever you use to talk to him/her.
//all your checks to see what (s)he will repair.
src.repairing=1 // I am assuming src is the player.
var/obj/repaireditem=switch(input(src,"What would you like to repair?","Repair","Cancel") in [list]//whatever you named the list of stuff he will repair.
if("Cancel")
return
src.repairing=0
else
//all stuff to determine the cost of the repair and whether you repair it or not.
src.repairing=0

client
North()
if(src.repairing)
src<<"Smith: Where do ye think yer goin?"
return
South()
if(src.repairing)
src<<"Smith: Where do ye think yer goin?"
return
//repeat for all other directions.


Hope that helps.

p.s. I left a mistake in there on purpose... it isn't really to hard to figure out, but I just didn't want to give you 100% free code >_>

§atans§pawn
In response to Satans Spawn
Satans Spawn wrote:
p.s. I left a mistake in there on purpose... it isn't really to hard to figure out, but I just didn't want to give you 100% free code >_>


Yeah, you gotta watch that Foomer. He's a shifty fellow. ;)
So why not lock their clicking, too? ;-)

mob
var
dblclick_lock = 0
click_lock = 0
move_lock = 0

client
DblClick()
if(!usr.dblclick_lock)
usr.dblclick_lock = 1
..()
usr.dblclick_lock = 0

Click()
if(!usr.click_lock)
usr.click_lock = 1
..()
usr.click_lock = 0

Move()
if(!usr.move_lock)
usr.move_lock = 1
..()
usr.move_lock = 0
In response to Elation
Elation wrote:
Satans Spawn wrote:
p.s. I left a mistake in there on purpose... it isn't really to hard to figure out, but I just didn't want to give you 100% free code >_>


Yeah, you gotta watch that Foomer. He's a shifty fellow. ;)

Not to mention that this is Design Philosophy, not Code Problems. A missing ")" isn't going to hurt me any because I'm not using his code anyway! :P
In response to Cinnom
You know that thought has honestly never occured to me before.
In response to Foomer
Foomer wrote:
Elation wrote:
Satans Spawn wrote:
p.s. I left a mistake in there on purpose... it isn't really to hard to figure out, but I just didn't want to give you 100% free code >_>


Yeah, you gotta watch that Foomer. He's a shifty fellow. ;)

Not to mention that this is Design Philosophy, not Code Problems. A missing ")" isn't going to hurt me any because I'm not using his code anyway! :P

On second thought... I left two mistakes in there on purpose... >_>

<_<

§atans§pawn
In response to Satans Spawn
Well if you're going to be that way I'll just redo the whole thing myself!

mob/var/repairing = 0

mob/npc/blacksmith
verb/Talk(mob/M)
usr.repairing = 1
var/obj/O = input(usr, "What would you like to repair?",
"Repair") as null|obj in usr.contents
if(O)
// Do repair stuff here.
usr.repairing = 0
return

client
Move()
if(mob.repairing)
src << "You can't move right now!"
return
return ..()


But that was pointless since I'm planning to go with Cinnom's approach anyway.
In response to Foomer
Another good thing to include is a "busy" variable. Generic enough that it can stop most actions from happening at the same time. var/busy="BlackSmith" would be a good example, once you are done with the blacksmith, set busy back to null. That way you can't fight, or go talk to 15 NPCs or whatever.
I'd use datums for alerts, inputs, and any other type of popups so that when a person moves, the datum can be deleted and therefore the popup. It'd suck not to be able to move until an popup is submitted.

I first figured to use something like this when I was using alerts to ask everyone if they wanted to hear a song. It was horrible because it couldn't alert everyone at once (although that now that I look back at it, adding spawn() might do the trick) and one person would have to answer the alert() for it to alert the next person.
Client interface has no shortage of frustration. You must always check to make sure and argument is valid before you mess with it and not assume it is valid just because a verb passed it.

For example verb/attack(mob/M as mob in oview(1)) should only let you attack adjacent mobs, right? But what about when you click attack and the mob walks away before you select it? Or the user just plain spoofs it? Well, in the attack routine, make sure you check range again. Same goes for any selection option. Stores need to make sure the buyer/seller is still in range, the item is still there with the same values, etc. before completing the transaction.

So in your example, the repair routine needs to check range and condition after the selection and spit out a message if something changed. ("You are too far away to complete the transaction!", "The object is more damaged than the smith thought!")

I don't like activity locks, personally, especially if they don't have a way to cancel! Let the player walk away if he wants. Just account for that in the system.
In response to CaptFalcon33035
That's a good idea too. Here's my attempt at that, which seems to work pretty well so far:

client/var/alert/alert = null

alert
proc/Result(mob/M, msg, title, button1, button2, button3)
if(!M.client) return null
if(M.client.alert) // so they can't stack up alerts.
del(M.client.alert)
M.client.alert = src
var/result = alert(M, msg, title, button1, button2, button3)
return result

client.Move()
if(src.alert)
del(src.alert)
return ..()

proc/DatumAlert(mob/M=usr, message="", title="", button1="Okay", button2=null, button3=null)
if(!M.client) return null
var/alert/A = new /alert (src)
var/result = A.Result(M, message, title, button1, button2, button3)
del(A)
return result

mob/verb/TestAlert()
src << DatumAlert(src, "Hey, this is my Alert test!", "Alert Test")
mob
var/freeze=0
Move()
if(freeze>0) return 0
else return ..()

//Stuff

proc/DoBlacksmithStuff(mob/m)
m.freeze++
//Do stuff
m.freeze-- //Window closed.
Foomer wrote:
There are so many ways to abuse the interface in BYOND games that it just aggrivates me. For example, I've got a little system where if you're next to a blacksmith, a statpanel will appear displaying any equipment he's willing to repair. When you double-click the equipment from there, you get a popup quoting you the repair cost and given a choice to accept the offer or not.

Well, interface abuse isn't centric to BYOND; Its something that is important to remember and test for no matter what you're doing.

Perhaps an article is in order ? ;)
In response to Alathon
Care to provide? I think it would be very nice to expose this to the public and the ways to prevent this. It'd be a real help to the community, but a real bug to the people who do not read the forums. (People who happen to notice the article will try to abuse this situation in other games.)
In response to Alathon
If I knew the code that went behind all the different ways there are to abuse BYOND interfaces, I'd be happy to provide one. :P But as it is, sometimes I can find abusable flaws in interfaces without knowing how they're programmed, and thus why the flaw exists, so I'd have no idea how to reproduce it.
In response to Jp
Thank you for totally not reading the last paragraph in my original post. :P
In response to Foomer
Foomer wrote:
Thank you for totally not reading the last paragraph in my original post. :P

That was in response to my post, not sure I'm getting the hint here, since your last paragraph was about opening multiple browser windows.

There are some rather simple solutions to a great deal of the problems described here.

1) Track what browser windows the player opens, and only allow one of each type you want open at any one time.

2) Verify the important aspects throughout every part; If its a sale, the players location as well as his finances, inventory space and similar are probably important to control both at the start and at the end of the purchase.

3) If need be, force the player to stop what he's doing if he wants to move.

4) Always verify that input is valid. Many games have gotten screwed by players buying negative amounts of items and similar.

And the most important one:

5) If your procedure sleeps or waits for user input at ANY point, never assume that variables are the same after that sleep or after the user has sent input. This includes EVERYTHING, up to the fact that the user might not even be online anymore.
Wouldn't the easiest fix be to just recalculate the cost when all the input is over?
Instead of using boolean variables, why not try this;

mob/NPC/Blacksmith
DblClick()
usr.MovementLocked++
//Input stuff here or however else you'll do the shop.
usr.MovementLocked--


So, every time the player opens the Blacksmith's shop, he gets 1 added onto his MovementLocked variale, and after he's finished, it's taken off. You could even go further and say, if he had MovementLocked at 20 or so he is auto banned for "Attempted bug abusing".

Just don't save the MovementLocked variable and you're good to go.

Oh, by the way, it's not tested at all, so I may have screwed up and left some bad code in there somewhere.
Page: 1 2