PDA

View Full Version : Help me weed out bad practices.


maximus_asinus
07-31-2017, 01:44 AM
So I was scripting in an online editor and was annoyed by the people popping into the level to see what I was working on. So I scripted a system to remove players froma level in the offline editor in GS1. As part of my practice learning GS2 I want input on mistakes I made, how to make the script more efficient, design security for the NPC, and finally convert the script into GS2. Any advice is appreciated.


// Script to add/remove players from ban list
if (playerchats) {
if (strequals(#a,maximus_asinus)) {
if (startswith(/add,#c)) {
tokenize #c;
this.duplicate = false;
if (strequals(#t(1),#a)) {
setplayerprop #c,You cannot ban yourself.;
}
else {
for (i=0;i<sarraylen(server.warp);i++;) {
if (strequals(#I(server.warp,i),#t(1))) {
this.duplicate = true;
}
}
if (this.duplicate == true) {
setplayerprop #c,"#t(1)" is already on the ban list.;
}
else {
addstring server.warp,#t(1);
setplayerprop #c,Added "#t(1)" to the ban list.;
}
}
}
if (startswith(/remove,#c)) {
tokenize #c;
for (i=0;i<sarraylen(server.warp);i++;) {
if (strequals(#I(server.warp,i),#t(1))) {
deletestring server.warp,i;
setplayerprop #c,Removed "#t(1)" from the ban list.;
}
}
}
}
}


// Script that removes the player
if (created) timeout = 0.05;
if (timeout) {
for (i=0;i<sarraylen(server.warp);i++;) {
if (strequals(#I(server.warp,i),#a)) {
setplayerprop #c,You're banned from this level.;
setlevel2 onlinestartlocal.nw,30,30;
}
}
timeout = 0.05;
}

I'm assuming placing a for loop inside a never ending timeout is bad practice and possibly process intensive. Can you manipulate another player's level and coordinates through a script like this? That way I could remove the player when I add them to the list and only have a playerenters check to see if they're on the list which would eliminate the timeout completely. This would also help me understand how you can manipulate other players via script.

Also I think I read that variables can now be assigned text values in GS2 like how strings work in GS1. Can you manipulate those variables in the same way as I am doing it in my script? If so, how?

MysticalDragon
07-31-2017, 02:26 AM
So I was scripting in an online editor and was annoyed by the people popping into the level to see what I was working on. So I scripted a system to remove players froma level in the offline editor in GS1. As part of my practice learning GS2 I want input on mistakes I made, how to make the script more efficient, design security for the NPC, and finally convert the script into GS2. Any advice is appreciated.


// Script to add/remove players from ban list
if (playerchats) {
if (strequals(#a,maximus_asinus)) {
if (startswith(/add,#c)) {
tokenize #c;
this.duplicate = false;
if (strequals(#t(1),#a)) {
setplayerprop #c,You cannot ban yourself.;
}
else {
for (i=0;i<sarraylen(server.warp);i++;) {
if (strequals(#I(server.warp,i),#t(1))) {
this.duplicate = true;
}
}
if (this.duplicate == true) {
setplayerprop #c,"#t(1)" is already on the ban list.;
}
else {
addstring server.warp,#t(1);
setplayerprop #c,Added "#t(1)" to the ban list.;
}
}
}
if (startswith(/remove,#c)) {
tokenize #c;
for (i=0;i<sarraylen(server.warp);i++;) {
if (strequals(#I(server.warp,i),#t(1))) {
deletestring server.warp,i;
setplayerprop #c,Removed "#t(1)" from the ban list.;
}
}
}
}
}


// Script that removes the player
if (created) timeout = 0.05;
if (timeout) {
for (i=0;i<sarraylen(server.warp);i++;) {
if (strequals(#I(server.warp,i),#a)) {
setplayerprop #c,You're banned from this level.;
setlevel2 onlinestartlocal.nw,30,30;
}
}
timeout = 0.05;
}

I'm assuming placing a for loop inside a never ending timeout is bad practice and possibly process intensive. Can you manipulate another player's level and coordinates through a script like this? That way I could remove the player when I add them to the list and only have a playerenters check to see if they're on the list which would eliminate the timeout completely. This would also help me understand how you can manipulate other players via script.

Also I think I read that variables can now be assigned text values in GS2 like how strings work in GS1. Can you manipulate those variables in the same way as I am doing it in my script? If so, how?

Yea timeout is definitely not needed you can just invoke playerenters and kick them.

I would create a database and when you create it place it in your level.


function onCreated() {
this.commandList = {
"account1",
"account2"
};
}

function onPlayerChats() {
if(!(player.account in this.commandList)) return;
if(player.chat == "/add") {
temp.user = player.chat.substring(5);
player.chat = "";
onAddPlayer(temp.user);
}
if(player.chat == "/remove") {
temp.user = player.chat.substring(8);
player.chat = "";
onRemovePlayer(temp.user);
}
}

function onAddPlayer(temp.user) {
if(!(temp.user in this.allowedUsers)) {
this.allowedUsers.add(temp.user);
this.chat = format("User %s has been added!", temp.user);
} else {
this.chat = format("User %s already added!", temp.user);
}
scheduleevent(3, "onResetChat");
}

function onRemovePlayer(temp.user) {
if(temp.user in this.allowedUsers) {
this.allowedUsers.remove(temp.user);
this.chat = format("User %s has been removed!", temp.user);
} else {
this.chat = format("User %s is not in allowlist!", temp.user);
}
scheduleevent(3, "onResetChat");
}

function onResetChat() {
this.chat = "";
}

function onPlayerEnters() {
if(!(player.account in this.allowedUsers)) {
player.chat = "I'm not allowed in this level!";
player.setlevel2("levelname.nw", x, y);
}
}



I didn't test this as I did it on the forums, but you should get a good idea on how to do it if it doesn't work. I added a couple methods that I normally wouldn't in such a small situation like format. But it does make your code cleaner and its good to learn it.

maximus_asinus
07-31-2017, 03:49 AM
Yea timeout is definitely not needed you can just invoke playerenters and kick them.
I only used the timeout to remove the player as soon as they were added to the ban list. Otherwise they could stay in the level until they left or reconnected.

Script
Okay so I understand how the script works, but I have a few questions. First, what is "ScheduleEvent"? Is that like how sleep worked in GS1? Second, what is "format"?

Thanks for replying.

MysticalDragon
07-31-2017, 03:57 AM
sleep(1) is more like waitfor(this, "none", 1) in GS2 the benefit of this it doesn't stop the execution of the script, that's my preferred method If its viable. scheduleevent can call a function on a timer basis. Basically It wont trigger the function immediately but when it hits the timer. Its better then timeout since it can be called once. Although I seen some people set there setTimer(0) (not sure if that even works, never tried it), which is ugly in my opinion. Reason I used this is because like your join classes, It's good to have one centralized place for repetitive code. instead of adding this.chat = "" after ever function its only in one place. Format just organized your code in a readability type way. Instead of doing things like,


echo("test1:" SPC temp.test1 SPC "test2:"SPC temp.test2 SPC "test3:"SPC temp.test3)

which looks horrible you could do


echo(format("test1: %s test2: %s test3: %s", temp.test1, temp.test2, temp.test3));


It also can be used to replace parts of a string. Without using replacetext which could be intensive in some instances.


temp.copy_id = player.account;
temp.level = findlevel(format("mylevel-%s.nw", temp.copy_id));

maximus_asinus
07-31-2017, 06:26 AM
Okay I'm having issues with the basics.


function onPlayerChats() {
if (player.chat == "Hello") {
this.chat = "true";
}
}

This will only return true if player.chat is "Hello" but not when it is "Hello World". And reading the script this is how it should behave. But in your example above you made it sound as if it would catch everything. Am I using it wrong?

MysticalDragon
07-31-2017, 07:07 AM
Accident, I meant to do
if(player.chat.starts("/add")) {
and
if(player.chat.starts("/remove")) {

maximus_asinus
07-31-2017, 11:13 AM
Okay so I made an attempt at scripting the same system in GS2 using Carlito's example as my base, but I couldn't get format to work with player.chat. Also tried to do something like this.chat = temp.list "message" but I couldn't get it to work either. Does anyone know why?

Eventually I want to display the people on the blacklist. I know I could do something simple like this.chat = this.deniedList, but the output looks pretty ugly. Is there a better method of displaying the contents of the array?


function onCreated() {
this.commandList = {"account1","account2"};
}
function onPlayerChats() {
if (player.account in this.commandList) {
if (player.chat.starts("/add")) {
temp.list = player.chat.tokenize()[1];
onAddPlayer(temp.list);

}
if (player.chat.starts("/remove")) {
temp.list = player.chat.tokenize()[1];
onRemovePlayer(temp.list);
}
}
}
function onAddPlayer(temp.list) {
if (temp.list in this.commandList) {
player.chat = "Cannot add admin to list";
}
else {
if (temp.list in this.deniedList) {
player.chat = "Account already on list.";
}
else {
this.deniedList.add(temp.list);
player.chat = "Account added to list.";
}
}
}

function onRemovePlayer(temp.list) {
if (temp.list in this.deniedList) {
this.deniedList.remove(temp.list);
player.chat = "Account removed from list.";
}
else {
player.chat = "Account not on list.";
}
}
function onPlayerEnters() {
if (player.account in this.deniedList) {
player.chat = "You were removed from this level.";
player.setlevel2("levelname.nw",x,y);
}
}

[EDIT]Also still need a method to remove players from the level if they're already inside.

maximus_asinus
07-31-2017, 11:23 PM
So I'm trying to think of a method to warp other players but I can't think of anything. The closest I can come up with is using triggeraction passing on temp.list, and then doing something like:


triggeraction("warp",temp.list);


function onActionWarp() {
if (player.account == temp.list) {
player.setlevel2("levelname.nw",x,y);
}
}

And I could see this working if the player had this as a weapon, but I'm working with a single NPC in a level and can't add a weapon to the player.

DustyPorViva
08-01-2017, 12:19 AM
In GS2 the most efficient way to warp players out of a level is to have a serverside NPC with a setshape that extends to the whole level, like so:


function onCreated() {
// Set the NPCs shape to 1024x1024, or 64x64*16
setShape(1,1024,1024);
// You can still set an NPC to not block and it will retain its shape and trigger onPlayerTouchsme
// This is useful for instances like this, while also not making the entire level return true for any serverside onwall() checks
dontblock();
}

// The player will trigger onPlayerTouchsme() as soon as they enter the level, and again if their position changes at all
// See below for explanation
function onPlayerTouchsme() {
// Don't warp out your account
if (player.account == "maximus_asinus") return;
// Don't warp out players who are in server.warp
if (player.account in server.warp) return;
// Warp player to the designated unstickme level
player.chat = "You're banned from this level";
player.setlevel2(serveroptions.unstickmelevel,serv eroptions.unstickmex,serveroptions.unstickmey);
}

No timeouts needed, no need to trigger the server. You can do the same thing on clientside as well but instead make the NPC block for players who aren't allowed in, denying them the ability to walk around. You can also do something like rendering a black showpoly on the screen so they can't see anything in the level.

The benefit of this method is letting the server do all the heavy-lifting so even if they are lagging they will still get warped out as soon as they enter the level.

Also, onPlayerTouchsme works differently on serverside and clientside. On serverside it will be triggered whenever the players position changes, not just when the player invokes movement. It will also be triggered multiple times, unlike clientside. Clientside onPlayerTouchsme will only trigger when the player himself moves and touches an NPC and it will won't trigger until the player moves away and touches the NPC again(save some glitchy methods). This means when dealing with serverside onPlayerTouchsme() it's fairly good at detecting players entering the level and moving without needing to loop or rely on clientside methods.

Kamaeru
08-01-2017, 12:42 AM
onPlayerTouchsme

Cool, I had noticed some of those nuances about onPlayerTouchsme via trial and error but it's good to hear it from someone who has already figured it out.

Can you give the same rundown for onPlayerEnters()? Is it different clientside and serverside?

Alsowhat is the benefit of making the server do heavy lifting? I would assume everyone uses a supercomputer these days so making as much stuff clientside would be the smart thing to do. For 2k1's server, it doesn't seem to be allocated very much memory and I had to cut the amount of birds flying in half to stop server lag.

maximus_asinus
08-01-2017, 02:00 AM
Serverside
Okay another novice question here. What is the difference between serverside and clientside NPCs? I assume that clientside NPCs are only run on locally and can have different behaviors for each person depending on the script, so does serverside run for everyone? How does the client know what NPCs are clientside or serverside? Is that the purpose of //#CLIENTSIDE?

And I still think it would be better to remove the player once I executed the command. Otherwise you could just stay still to prevent being warped. But maybe this is a limitation of not making it a weapon.

DustyPorViva
08-01-2017, 03:52 AM
Cool, I had noticed some of those nuances about onPlayerTouchsme via trial and error but it's good to hear it from someone who has already figured it out.

Can you give the same rundown for onPlayerEnters()? Is it different clientside and serverside?

Alsowhat is the benefit of making the server do heavy lifting? I would assume everyone uses a supercomputer these days so making as much stuff clientside would be the smart thing to do. For 2k1's server, it doesn't seem to be allocated very much memory and I had to cut the amount of birds flying in half to stop server lag.
onPlayerEnters() works the same both serverside and clientside I'm pretty sure, though it's finicky when you're dealing with gmaps.

What I mean about letting the server do the heavy lifting is that you don't need to worry about their computer, connection issues/lag, or things like that. The server will know they entered the level and they will trigger onPlayerTouchsme() and get warped out before their PC even gets the data. It's just faster and more efficient than detecting it clientside and then waiting for the data to get to the server and then sending it. And if they're lagging it may not happen for a while or at all. An event like onPlayerTouchsme() won't do much, while scripts like birds that are always running loops will be much more intensive.

By the way, NPCs running loops will continue to run loops even if a player is not in a level. I don't think the loop is stopped until the level is pulled from memory and cleaned up... and I think that's like 5-10 minutes. It's best to break any loops serverside(such as a timeout loop) when players.size() <= 0. Something like this:

function onPlayerEnters() {
// Resume the loop, clear the idle flag.
if (this.idle) setTimer(.1);
this.idle = false;
}
function onTimeout() {
// No players so don't waste time doing things
if (players.size() <= 0) {
this.idle = true;
return;
}
// Do loop stuff
setTimer(.1);
}
It will cut down on a lot of unnecessary processing occurring in inactive levels. Anything that runs on a constant loop should implement this sort of check. The only downside is that players.size() can sometimes be faulty causing NPCs to stop working because it thinks there are no players around. You can get around this by trying players.size() <= 0 && this.level.findareaplayers(0,0,64,64).size() <= 0 but I don't think it's necessary.

Okay another novice question here. What is the difference between serverside and clientside NPCs? I assume that clientside NPCs are only run on locally and can have different behaviors for each person depending on the script, so does serverside run for everyone? How does the client know what NPCs are clientside or serverside? Is that the purpose of //#CLIENTSIDE?

And I still think it would be better to remove the player once I executed the command. Otherwise you could just stay still to prevent being warped. But maybe this is a limitation of not making it a weapon.
Serverside script is run on the server, while clientside script is run on the players device. Anything clientside is being run on the players machine, therefor it can be accessed via memory editing and such. Anything secure should be done on the server. This is why it's better to do your player detection on the serverside instead of client, because depending on circumstances they might be able to bypass whether or not the script detects them there. Serverside doesn't need to worry about that, the player can't access anything there.

And yes, it works like this:

function onCreated() {
echo("This is serverside code. This will appear in RC, since it's being handled by the server.");
}
//#CLIENTSIDE
function onCreated() {
echo("This is clientside code. This will appear in the F2 log window since it's being handled by the players machine.");
}


As you can see, even though you would normally not be able to have duplicate functions that will work fine because they're not even being parsed by the same machine. They work indepedently of each other, and in order to communicate between them you will need to send data to the server or have the server send data to the player via triggeraction()/triggerserver()/triggerclient(). Though there are other ways to communicate, via relying on synchronized variables like player.client/clientr.flags and player/npc.attr[1-30].

Anyways about the onPlayerTouchsme(). You don't need to worry about players entering the level and not moving. Like I said on serverside this gets called whenever the player position changes--that includes when they change position by entering the level. As soon as they enter the level it will trigger onPlayerTouchsme(). The good thing about this over onPlayerEnters() is that if for whatever reason it may fail, if they move at all they will trigger it again. And again, if you're that worried you can put a polygon on the clientside so they can't see anything anyways. Like so:

function onCreated() {
// Set the NPCs shape to 1024x1024, or 64x64*16
setShape(1,1024,1024);
// You can still set an NPC to not block and it will retain its shape and trigger onPlayerTouchsme
// This is useful for instances like this, while also not making the entire level return true for any serverside onwall() checks
dontblock();
}

// The player will trigger onPlayerTouchsme() as soon as they enter the level, and again if their position changes at all
// See below for explanation
function onPlayerTouchsme() {
// Don't warp out your account
if (player.account == "maximus_asinus") return;
// Don't warp out players who are in server.warp
if (player.account in server.warp) return;
// Warp player to the designated unstickme level
player.chat = "You're banned from this level";
player.setlevel2(serveroptions.unstickmelevel,serv eroptions.unstickmex,serveroptions.unstickmey);
}
//#CLIENTSIDE
function onCreated() {
setShape(1,1024,1024);
}

// Since onPlayerTouchsme() doesn't quit work like it does serverside it's best to just use onPlayerEnters()
function onPlayerEnters() {
if (player.account == "maximus_asinus" || player.account in server.warp) {
// Let's not block players who are allowed in the level
dontblock();
hideimg(200);
return;
}
// Anyone who gets to this part of the code isn't allowed in the level. The NPC will
// continue to block the level. Let's also render a polygon to obscure their vision

with (findimg(200)) {
// A polygon shape that fills the entire screen
polygon = {0,0,screenwidth,0,screenwidth,screenheight,0,scre enheight};
// Make it black
red = green = blue = 0;
// A high layer
layer = 10;
}
}

maximus_asinus
08-01-2017, 07:06 AM
thank you Dusty, that is very helpful to know.

cbk1994
08-01-2017, 07:07 AM
In GS2 the most efficient way to warp players out of a level is to have a serverside NPC with a setshape that extends to the whole level, like so:

This is a really neat trick, but I'm curious why you'd prefer this over a serverside onPlayerEnters?

MysticalDragon
08-01-2017, 09:00 AM
This is a really neat trick, but I'm curious why you'd prefer this over a serverside onPlayerEnters?

My guess is because the player is already in the level, thus playerenters not being invoked.

DustyPorViva
08-02-2017, 01:51 AM
This is a really neat trick, but I'm curious why you'd prefer this over a serverside onPlayerEnters?

Mostly picked it up from using it on gmaps, since I can throw it in a class and put it in levels on an overworld, snap it with x = int(this.x/64)*64; and it will still warp players out of specific levels on the gmap, since onPlayerEnters() doesn't work. I also just find it more reliable since it will persist even after the initial entering the level.

Kamaeru
08-02-2017, 10:10 PM
Mostly picked it up from using it on gmaps, since I can throw it in a class and put it in levels on an overworld, snap it with x = int(this.x/64)*64; and it will still warp players out of specific levels on the gmap, since onPlayerEnters() doesn't work. I also just find it more reliable since it will persist even after the initial entering the level.

That's how zones are handled on 2k1 like event zones (survivor, CNR, etc), kingdoms zones, nopk zones, quest zones, etc.

maximus_asinus
08-03-2017, 05:52 AM
Without having a weapon added to another player, is it possible to manipulate another player who is not in the same level as me?

maximus_asinus
08-08-2017, 04:16 AM
I'm trying to use attachplayertoobj.

if (created) {
setshape 1,256,128;
}
if (playertouchsme) {
attachplayertoobj 0,id;
}

Offline that works flawlessly, but online if I touch the NPC my coordinates get set to 0 and I'm warped to the corner of the level. What am I doing wrong?

MysticalDragon
03-17-2018, 08:26 PM
this is a bit late try doing it clientside online

Kamaeru
03-18-2018, 05:24 AM
I'm trying to use attachplayertoobj.

Ok I actually figured that out recently. You have to set the positions for entering/exiting as a square area. Something like this in the case of a bridge I scripted for Dustari:


function onCreated() {
this.isbridge = true;
dontBlock();
drawoverplayer();

}

//#CLIENTSIDE
function onCreated() {
this.bridgeWidth = 12;
this.bridgeHeight = 9;
this.bindPositions = {
{3, {-1,3.5}, {1,4.5}}, // {dirToBindOn, {x1,y1}, {x2, y2}}
{1, {11,3.5}, {13,4.5}}
};
this.tilesOver = {
22,22,22,22,22,22,22,22,22,22,22,22,
22,22,22,22,22,22,22,22,22,22,22,22,
22,22,22,22,22,22,22,22,22,22,22,22,
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
22,22,22,22,22,22,22,22,22,22,22,22,
22,22,22,22,22,22,22,22,22,22,22,22,
22,22,22,22,22,22,22,22,22,22,22,22,
22,22,22,22,22,22,22,22,22,22,22,22,
};
this.tilesUnder = {
22,22,22,22,0 ,0 ,0 ,0 ,22,22,22,22,
22,22,22,22,0 ,0 ,0 ,0 ,22,22,22,22,
22,22,22,22,0 ,0 ,0 ,0 ,22,22,22,22,
22,22,22,22,0 ,0 ,0 ,0 ,22,22,22,22,
22,22,22,22,0 ,0 ,0 ,0 ,22,22,22,22,
22,22,22,22,0 ,0 ,0 ,0 ,22,22,22,22,
22,22,22,22,0 ,0 ,0 ,0 ,22,22,22,22,
22,22,22,22,0 ,0 ,0 ,0 ,22,22,22,22,
22,22,22,22,0 ,0 ,0 ,0 ,22,22,22,22,
};
setShape2(this.bridgeWidth, this.bridgeHeight, this.tilesUnder);
setImg("g2k1_dustari_castlebridge.png");
}

function onPlayerEnters() { this.setTimer(0.05); }
function onTimeout() {
temp.pcx = player.x + 1.5;
temp.pcy = player.y + 2;
temp.inzone = 0;
for (temp.i: this.bindPositions) {
temp.cdir = temp.i[0];
temp.cx1 = this.x + temp.i[1][0];
temp.cx2 = this.x + temp.i[2][0];
temp.cy1 = this.y + temp.i[1][1];
temp.cy2 = this.y + temp.i[2][1];
if (temp.pcx in |temp.cx1, temp.cx2| && temp.pcy in |temp.cy1, temp.cy2|) {
if (player.attached && player.dir != temp.cdir)
leaveBridge();
else if (!player.attached && player.dir == temp.cdir)
enterBridge();
temp.inzone = 1;
}
}

this.setTimer(0.05);
}
function leaveBridge() {
drawunderplayer();
setShape2(this.bridgeWidth, this.bridgeHeight, this.tilesUnder);
detachPlayer();
sleep(.2);
drawoverplayer();

}
function enterBridge() {

setShape2(this.bridgeWidth, this.bridgeHeight, this.tilesOver);
attachPlayerToObj(0, id);
}


The important thing to look at being this.bindpositions because it is what tells the timeout function where to check. It took me a while to figure this out, but this script can be modified to create a nice illusion of layering in many situations.

MysticalDragon
03-18-2018, 08:12 PM
We did the same thing on delteria but I like your method you did with the array regarding setShape2 I did that aswell. Also use tags instead of code tags looks much cleaner.


[PHP]
//#CLIENTSIDE
function onCreated() {
with (findimg(200)) {
this.image = thiso.bridgeImage;
this.layer = 1;
this.x = thiso.x;
this.y = thiso.y;
this.mode = 1;
this.alpha = 0.75;

if (thiso.direction == "vertical")
this.y += 2;
else
this.x += 2;
}

this.bridgeWidth = getImgWidth(this.bridgeImage) / 16;
this.bridgeHeight = getImgHeight(this.bridgeImage) / 16;
this.w = this.bridgeWidth + (this.direction == "horizontal" ? 4 : 0);
this.h = this.bridgeHeight + (this.direction == "vertical" ? 5 : 0);

// 10 16
// 10 21

this.dontBlock();

onTimeOut();
}

function onBridge(temp.py) {
if (player.x in |this.x, this.x + this.w|) {
if (temp.py in |this.y, this.y + this.h|)
return true;
}

return false;
}

function isPlayerEntering(temp.py) {
if (player.x in |this.x, this.x + this.w|) {
if (temp.py in |this.y, this.y + 2|)
return true;

if (temp.py in |this.y + this.h - 2, this.y + this.h|)
return true;
}

return false;
}

function onTimeOut() {
temp.py = player.y + 2 + vecy(player.dir) * 1.05;

temp.entered = isPlayerEntering(temp.py);

if (temp.entered) {
if (player.attached == false) {
attachPlayerToObj(0, this.id);
setshape2(this.w, this.h, this.shape);
}
} else if (player.attached == true && player.attachid == this.id) {
if (onBridge(temp.py)) {
} else {
detachplayer();
}
this.chat = "attached";
} else {
this.chat = "na";
}

setTimer(0.05);
}