PDA

View Full Version : Point and Copy: Cloning Device


Gambet
04-04-2007, 09:03 PM
//Point and Copy: Cloning Device
//By: Gambet

//#CLIENTSIDE
function onKeyPressed(keycode)
{
if (keycode == 305) //Press and hold shift then press 1
{
this.cloner_on = !this.cloner_on;
player.chat = "Outfit Cloner: " @ (this.cloner_on ? "On" : "Off");
}
}

function onMouseDown(button)
{
if (button == "double")
{
if (this.cloner_on)
{
if (timevar2 >= this.cooldown)
{
for (p: players)
{
if (mousex in |p.x,p.x+1.5| && mousey in |p.y,p.y+2|)
{
for (t=0; t<5; t++)
{
player.colors[t] = p.colors[t];
}
player.headimg = p.headimg;
player.shieldimg = p.shieldimg;
player.bodyimg = p.bodyimg;
player.chat = "Cloning Successful!";
this.cooldown = int(timevar2 + 10);
}
}
for (n: npcs)
{
if (mousex in |n.x,n.x+1.5| && mousey in |n.y,n.y+2|)
{
for (u=0; u<5; u++)
{
player.colors[u] = n.colors[u];
}
player.headimg = n.headimg;
player.shieldimg = n.shieldimg;
player.bodyimg = n.bodyimg;
player.chat = "Cloning Successful!";
this.cooldown = int(timevar2 + 10);
}
}
}
else
{
player.chat = "You must wait " @ abs(int(timevar2 - this.cooldown)) @ " more seconds!";
}
}
}
}




I havn't tested it, but it should work.


Now, from the looks of it, it's a simple NPC, yes, but it's good enough for the new scripters to learn some of the basics.


Let me break it down:


This NPC shows a good way of using the keypressed function as a source for finding keycodes by designating such in the functions parameters. I used a weird combination to show that it's possible to use key combinations to perform different tasks.

This NPC also shows a good way of setting up a variable to be defined as the mouse button that the player presses in the mousedown function. You can basically put anything inside the parameters and use it to read if the player has pressed the "left" mouse button, "right" mouse button, "double" mouse button (I.E double click), and "middle" mouse button (I.E pressed on the scroll ball). Once you define this, reading what mouse button the player clicked is easy, as displayed above.

This NPC also shows a good way of using timevar2 as a cooldown device, without the need of using a timeout. You can use timevar2 for most cooldown systems as an alternative to timeouts, which will decrease the amount of CPU time that your system takes. As you see here, I created a cooldown system without the need of a timeout, which is more efficient when it comes to lag.

This NPC also shows a way of looping through all of the players and NPCs in order to perform certain tasks that you'd wish to designate.

This NPC also shows how you can manipulate certain events that will in turn cause an NPC and/or player to act as you'd like. In this case, I check to see if when the player double clicks their mouse, if their mouse position is within a certain range of a player or NPC, then the person will end up copying the outfit of the player or NPC. You can use this method to perform plenty of tasks, not just copying attire.

And finally, this NPC also shows a good way of using a loop to loop through a player/npcs clothes colors without the need of separate this.colors[0], this.colors[1], etc. lines. You can use loops to do a lot of things much faster and more efficiently, such as when reading what keys the player pressed and so forth.


Though it is simple to those experienced, it can actually teach the newer scripters quite a few things on how to work with several functions and so forth to make scripting a bit easier and more efficient. I scripted this NPC simply for educational purposes, though it can also be used for what it is, which is a cloning device.


NOTE: If the designated NPC is not a character, then your outfit will be set to the default noob outfit.



Hope this helps,


Enjoy ^^

Skyld
04-04-2007, 09:46 PM
Just at a quick glance:
if (!this.cloner_on)
{
this.cloner_on = true;
player.chat = "Outfit Cloner: On";
}
else
{
this.cloner_on = false;
player.chat = "Outfit Cloner: Off";
}
... had might as well be:
this.cloner_on = !this.cloner_on;
player.chat = "Outfit Cloner: " @ (this.cloner_on ? "On" : "Off");
Also.
for (p: players)
{
for (n: npcs)
{
Do you need to scan every NPC for every player? Can't you just scan them once?

Finally, can't you prefix your temp vars with temp.? :(

Gambet
04-04-2007, 09:51 PM
... had might as well be:
this.cloner_on = !this.cloner_on;
player.chat = "Outfit Cloner: " @ (this.cloner_on ? "On" : "Off");


I'd actually appreciate it if you'd explain that to me since I never understood how that worked.


Also.
for (p: players)
{
for (n: npcs)
{
Do you need to scan every NPC for every player? Can't you just scan them once?


It scans all of the players in the level and all of the npcs in the level if you have the npc on and you double click. It does this to be able to compare the person that triggered its mouse position to see if it falls within the range to clone the object.

I don't seem to be understanding where you're coming from, mind elaborating a bit?


Finally, can't you prefix your temp vars with temp.? :(

I guess ^^

But does it make a difference? From my experience this. temp vars can be used throughout the NPC whereas temp vars can't unless you send them over through the parameters of a function.

Skyld
04-04-2007, 09:58 PM
I'd actually appreciate it if you'd explain that to me since I never understood how that worked.
Sure.
temp.var = !temp.var;
If you are working with just true and false, then this is basically a switch. Because temp.var uninitialized is 0 (false), then !0 is 1 (true), therefore !temp.var == true. Now, if temp.var is 1 (true), then !1 is 0 (false), therefore !temp.var == false.

echo(temp.foo ? "bar" : "baz");
This basically just checks if temp.foo (or any expression, even player.chat == "foo" or so) is true, and returns "bar" if it's true, and "baz" if it isn't.
It scans all of the players in the level and all of the npcs in the level if you have the npc on and you double click. It does this to be able to compare the person that triggered its mouse position to see if it falls within the range to clone the object.

I don't seem to be understanding where you're coming from, mind elaborating a bit?
Well, your loop appears to be scanning every NPC in the level for every player in the level.

Say me, you and Stefan are in the level. It'll first pick me up, and scan every NPC. It'll then move on to you, and scan every NPC. It'll finally move onto Stefan and scan every NPC.

I don't think you need to scan the NPCs as many times as there are players in the level.
I guess ^^

But does it make a difference? From my experience this. temp vars can be used throughout the NPC whereas temp vars can't unless you send them over through the parameters of a function.
this. variables are accessible in the NPC, and temp. variables are destroyed at the end of the function. If you don't need the variable after the end of the function, then just use temp.

Gambet
04-04-2007, 10:04 PM
Sure.
temp.var = !temp.var;
If you are working with just true and false, then this is basically a switch. Because temp.var uninitialized is 0 (false), then !0 is 1 (true), therefore !temp.var == true. Now, if temp.var is 1 (true), then !1 is 0 (false), therefore !temp.var == false.

echo(temp.foo ? "bar" : "baz");
This basically just checks if temp.foo (or any expression, even player.chat == "foo" or so) is true, and returns "bar" if it's true, and "baz" if it isn't.



Ah, thanks. Will try to keep that in mind from now on.



Well, your loop appears to be scanning every NPC in the level for every player in the level.

Say me, you and Stefan are in the level. It'll first pick me up, and scan every NPC. It'll then move on to you, and scan every NPC. It'll finally move onto Stefan and scan every NPC.

I don't think you need to scan the NPCs as many times as there are players in the level.



How would you recommend doing it then? Separating the loops?

Rapidwolve
04-04-2007, 10:04 PM
echo(temp.foo ? "bar" : "baz") that is a called conditional.
if-then-else

So if temp.foo is true, then that code will output "bar" else it will output "baz"

Skyld
04-04-2007, 10:05 PM
How would you recommend doing it then?
Well, presumably you would want to run over the players once, and THEN over the NPCs in the level, so you could have your player loop processing players, and then your NPC loop afterwards processing NPCs.

Gambet
04-04-2007, 10:07 PM
Well, presumably you would want to run over the players once, and THEN over the NPCs in the level, so you could have your player loop processing players, and then your NPC loop afterwards processing NPCs.


Yeah, I updated the post where I asked how you would do it asking if I should separate the loops, which is what you ended up saying anyways :p


I guess I'll do that.



EDIT: Updated first post

Kristi
04-04-2007, 10:42 PM
Skyld threw in his critques but ill put mine in anyway

//Point and Copy: Cloning Device
//By: Gambet
//Notes by Hell Raven

//#CLIENTSIDE
function onKeyPressed(keycode) {
if (keycode == 305) {
// If possible, we do not want to test unneccessary conditions
// !true = false, and !false = true, so this will make if the opposite
// of what it is.
this.cloner_on = !this.cloner_on;

// Now we are setting the player's chat to Outfit Cloner: Off or On
// We have an array containing {"Off","On"}, where off is
// element 0, and on is element 1. False = 0 and True = 1,
// so this works.
player.chat = "Outfit Cloner:" SPC ({"Off","On"})[this.cloner_on];
}
}

function onMouseDown(button) {
// Condition really belongs on one line
// Check the most likely false condition first
// To optimize resources.
if (button == "double" && this.cloner_on) {

// Cut short if we have not "cooled down" yet.
if(timevar2 < this.cooldown) {
player.chat = "You must wait " @ abs(int(timevar2 - this.cooldown)) @ " more seconds!";
return; //Exit function
}

// You originally had the npc loop nested inside the player loop.
// That means you checked all the npcs again everytime
// you switched to a different player. That is bad :x

// See if any player is in clone range
for (p: players)
if (mousex in |p.x,p.x+1.5| && mousey in |p.y,p.y+2|) {
cloneobj(player,p);
this.cooldown = timevar2 + 10;
player.chat = "Cloning Successful!";
return; // We have cloned so we do not need to look anymore.
}

// See if any npc is in clone range
// note, we check for ani so we do not clone npcs that aren't showchars.
for (n: npcs)
if (n.ani != null && mousex in |n.x,n.x+1.5| && mousey in |n.y,n.y+2|) {
cloneobj(player,n);
this.cooldown = timevar2 + 10;
player.chat = "Cloning Successful!" @ n.ani;
return; // We have cloned so we do not need to look anymore
}
}
}

function cloneObj(copycat,original) {
// makes copycat look like original :D

// the colors array cannot be set at once, for some reason
for(temp.i = 0; temp.i <=5; temp.i++)
copycat.colors[temp.i] = original.colors[temp.i];

copycat.head = original.head;
copycat.sword = original.sword;
copycat.body = original.body;
copycat.shield = original.shield;
}


You should make functions for certain things, like a player clone. honestly it should be a class attacked to a player, to do player.clone(obj), but we are keeping it in one weapon.

The nested loop was something i pointed out to skyld. isntead of
for(players) {
for(npcs) {
}
}
we want
for (players) {
}
for (npcs) {
}

We want to return when we have achieved our goal, so we are not achieving it multiple times (in my opinion).

Also, i threw in a check to make sure the npc was a character.

The rest of my notes are in the script.

Gambet
04-04-2007, 11:07 PM
"Conditions belong on one line" is completely opinion based and it all depends on the scripters style.


I, for one, find it more efficient to style as I do simply because it's easy to go back and edit things or add on things if needed. If you put the condition bracket on the same line then you fall in risk of losing your bracket positioning and possibly missing brackets or having too many.

Plus, this way looks a lot more clean IMO.


As for the character check for the NPCs, it doesn't really matter, since if it's not a character it'll set you to the default noob body, which isn't exactly a problem or anything. It's a fast way to get the noob body on for people that want it, though :p


Also, I try to separate condition statements for a reason. I don't really care about line length, moreso I care about flexibility. By separating your condition statements, it makes it easy to add else checks later on if you need to, without having to separate it at a later date and re-align the entire script just because you wanted to save a few lines in the beginning. It's not really lengthy at all with the way I style since everything is clearly presented. It's just easier to manage if you need to go back to it later on and fix or add-on anything.

Kristi
04-04-2007, 11:09 PM
I guess nested Ifs come down to mere preference, I like not tabbing my code that far over.