Scripting Tutorial 04

From Osgrid Wiki
Jump to: navigation, search
[12:13]  Fu Barr: okay - so from the top...
[12:13]  Fu Barr: the colours are part of something that coders/scripters in RL know as 'syntax highlighting'
[12:13]  Tannoy OSgrider: colors are there to make syntax more clear
[12:14]  Fu Barr: it is supposed to help the scripter/coder/programmer/abominable snowman to understand what he/she is writing
[12:14]  Fu Barr: so off the top of my head:
[12:14]  Fu Barr: orange text = comment
[12:14]  Fu Barr: green text = "some string of words"
[12:14]  Tannoy OSgrider: also to help with typo's lol
[12:15]  Fu Barr: BLUE_CAPS = some LSL pre-defined constant. like: ALL_SIDES or NULL_KEY
[12:15]  Fu Barr: purple text = keywords
[12:15]  Fu Barr: issit purple? too lazy to check... am just typing into chat
[12:15]  Koni Lanzius: :)
[12:16]  Object: Script running
[12:16]  Total Sorbet: blue i think
[12:16]  Fu Barr: so that's more or less what the colours mean
[12:16]  Koni Lanzius: ~**LOL**~
[12:16]  Koni Lanzius: ahem, there will be a quiz, headmaster! ;D
[12:16]  Pedro Edenflower is Online
[12:16]  Fu Barr: they are trying to help you 'read' your code. and on the whole it's a good thing
[12:16]  Total Sorbet: red = keywords
[12:16]  Fae Peep: are we looking at script v4 that we were looking at last time?
[12:16]  Koni Lanzius: ahh
[12:16]  Fu Barr: thanks total :)
[12:16]  Total Sorbet: maroon maybe
[12:16]  Object shouts: Script running
[12:17]  Fu Barr: well i was going to ask you - i'm happy to go back into script v0.4 or we can do something different.
[12:17]  Object shouts: Hey You
[12:18]  Fu Barr: i do believe that it's good to review stuff... and aloow people to ask questons on older material... but we can also go into the fun new stuff which may well be my very useful getValue() function
[12:18]  Koni Lanzius: hehehe
[12:18]  Koni Lanzius: ooo i love it when u talk codey
[12:18]  Fu Barr: which helps turn a list variable into a primitive 'dictionary' or key->value hash table of sorts.
[12:18]  New Styler: talk dirty?
[12:18]  Wizard Atazoth: haha
[12:19]  Fae Peep: the values and such
[12:19]  Fae Peep: and what they do
[12:19]  Fu Barr: particle script is v simple - it just feeds a list of parameters into llParticleSystem()
[12:19]  Fu Barr: the various wiki pages to that monster list are pretty clear
[12:20]  Fu Barr:
[12:20]  Jeff Hall: haha
[12:20]  Jeff Hall: 3 times
[12:20]  Total Sorbet: hehe
[12:20]  New Styler:
[12:20]  Fu Barr: :)))
[12:21]  Tannoy OSgrider: I guess that I was slowest in opening browser lol
[12:21]  Fu Barr: okay - so just because I believe in review, let's have a looksey at v0.4 of the door script again, we'll run past the concepts and let people ask questions and then we'll look at the list variable as a dictionary if we have time.
[12:22]  Fu Barr: the door script is at:
[12:22]  Object: Script running
[12:22]  Fu Barr:
[12:22]  Fu Barr: at the bottom of the page
[12:22]  New Styler: nice texture jeff :P
[12:22]  Fu Barr: or towards the end of the page
[12:22]  Jeff Hall: always:)
[12:22]  Fu Barr: thanks to total, dan and foxx who got it up there
[12:23]  Fu Barr: also the chat log of the previous 3 sessions is there on the wiki - for those of you who weren't able to join
[12:23]  Fu Barr: it's a straight dump of the chat log, so it's full of typonese.
[12:23]  Fu Barr: you'll hav to read round it... :)
[12:24]  Fu Barr: okay i'll give everybody a minute to get the 0.4 script and open it. no need to drop it into a prim.
[12:24]  New Styler: uhm wait i dont think i have it
[12:25]  Fu Barr: who hasn;t got the script open yet?
[12:25]  New Styler: nme
[12:25]  New Styler: wonders where to look
[12:25]  Azi Az: i havent the scripts i wasnt here last time
[12:25]  Fu Barr: okay - run to the URL above and get the thing :)
[12:25]  New Styler: duhhhh i just have to click on it lol
[12:25]  Total Sorbet: swingeryness? hehe
[12:26]  Fu Barr is not saying anything....
[12:26]  Fu Barr: everybody got the script open now?
[12:26]  Fu Barr: yes - it's at the end of the talk azi :)
[12:27]  Jeff Hall: i can see many nice colours
[12:27]  New Styler: just miss the pink color :(
[12:27]  Fu Barr: okay let's run through it.... if you have a hard time following - it's really the culmination of the three chats on the wiki - i suggest you just read them again later.
[12:27]  Azi Az: on end of talk...........i see a talk lol
[12:27]  Fu Barr: ookay here we go
[12:28]  Fu Barr: the top of the script has a short 'this is what the script does' description
[12:28]  Fu Barr: it;s in orange - ie. it' all comments
[12:28]  Fu Barr: it's my personal convention to add some info about what this version adds to the script
[12:28]  Fu Barr: so all this v0.4 does over v0.3 is add an actual swinging door
[12:29]  Azi Az: slaps Fu head
[12:29]  Koni Lanzius: haha
[12:29]  Fu Barr: then the next 20-30 lines are my variable definitions
[12:29]  Fu Barr: or at least the ones i think i need to be 'global'
[12:29]  Fu Barr: anybody unclear about what globals are?
[12:30]  Fu Barr: if yes - they are variable who values is available at any place in the script
[12:30]  Total Sorbet: tarty variables?
[12:30]  Fu Barr: *who's
[12:30]  Fu Barr: yeah - slutty vars.
[12:30]  Fu Barr: don't care who reads or writes them
[12:30]  Fu Barr: as you can see - i like to organise my code in several ways...
[12:30]  Fu Barr: again this is a personal style
[12:31]  Fu Barr: not 'how it must be done'.
[12:31]  Fu Barr: by this way hearts my brain less
[12:31]  Fu Barr: *hurts
[12:31]  Jeff Hall: the interesting part and new  for class are LISTS
[12:31]  New Styler: it does keep things clear to understand
[12:31]  Fu Barr: lol - jyes jeff we're getting to that :)
[12:32]  Fu Barr: so we have a section of txt_* vars - they contain blabla text strings. easy.
[12:32]  Fu Barr: then we have a few door_* vars they contain data for the door. duh.
[12:32]  Fu Barr: then...
[12:33]  Fu Barr: we have some CONSTANTS we'll see how they make my code easier to understand WITHOUT comments every line. less ORANGE in the code :)
[12:33]  Fu Barr: then finally we have some sec_* variables,,, they all deal with security - ie. the locking mechanism of the door
[12:33]  Fu Barr: again the labels are completely my invention
[12:34]  Fu Barr: if you want to use pink_*, gold_*, blue* as the way that works to keep your variables organised...
[12:34]  Fu Barr: be my guest
[12:34]  Fu Barr: the point is that if you give your variables good names, you need to use less comments
[12:34]  Fu Barr: now
[12:35]  Fu Barr: the next 50-70 lines are a few functions
[12:35]  Fu Barr: i wrote those to make my code easier to understand
[12:35]  Fu Barr: i could have left them in the places inthe code where i call them.... but then my code would be REPEATING itself. that's v bad
[12:36]  Fu Barr: always try to write your code once, and if you need to use the same of similar code twice or more times, make it a function so you can call it
[12:36]  Fu Barr: so there's:
[12:36]  Fu Barr: sec_acl_check()
[12:37]  Fu Barr: that function checks if the person trying to lock/open the latch is allowed to do that
[12:37]  New Styler: ok mind if i ask something/\
[12:37]  Fu Barr: sure - ask away!
[12:37]  New Styler: im very new so here i go
[12:37]  Fu Barr: np - dont be shy. we'll just mock you mercilessly... that's all :)
[12:37]  New Styler: that line you just posted how do i make sense of that like how do i know what to use
[12:38]  Fu Barr: well, there's many ways to answer that question
[12:38]  Fu Barr: i'll try and be as brief as i can and still make sense :)
[12:38]  Fu Barr: 1. most code is written in little 'chunks'
[12:38]  Fu Barr: each chunck does something
[12:39]  Fu Barr: some of these chunks are so common, that the people who provide the platform or language you're using sort of make them for you.
[12:39]  Fu Barr: this is what linden labs did for us with LSL
[12:39]  Fu Barr: chucnks like this are called 'functions'
[12:40]  Fu Barr: and the linden lab default comes in the box just add batteries functions so a whole lot of stuff
[12:40]  Koni Lanzius: ahh
[12:40]  Jeff Hall: like llSay for example
[12:40]  Fu Barr: but sometimes you discover that you have your own code chunks
[12:40]  Fu Barr: exactly
[12:40]  New Styler: well my guess they dont like things easy cause lsl doesnt make sense
[12:41]  Fu Barr: lol - i hate to be contrary but LSL make a lot of sense :)
[12:41]  PcTek CyberStar: lsl is easy... wanna try something hard, try c++ or assembly language... those are more difficult.
[12:41]  Fu Barr: anyhow - the idea here is that you have functions that are provied by linden labs and functions that you write yourself
[12:42]  Jeff Hall: visual basic is prob the most HUMAN READABLE code Styler but LSL still makes sense
[12:42]  Fu Barr: the sec_acl_check() and door_swing() functions are chunks of code that I wrote.
[12:43]  Fu Barr: and we'll see in the rest of the script how/where they are used.
[12:43]  PcTek CyberStar: mmm i'd say cobol... example: ADD TAX TO SUBTOT GIVING TOTAL;
[12:43]  Fu Barr: i hope that sort of answers your question - though i know it doesnt
[12:43]  Fu Barr: but to fully answer it i'd have to repeat the 1st 3 sessions again :)
[12:43]  Fu Barr: perhaps you read the wiki log trace of them and if you have questions - just IM me?
[12:43]  Fu Barr: okay - back to the script
[12:44]  Fu Barr: so, after the two functions sec_acl_check and door_swing... we have the states
[12:44]  Fu Barr: 3 states
[12:44]  Fu Barr: 1. default
[12:44]  Fu Barr: 2. closed
[12:44]  Fu Barr: 3. open
[12:44]  Fu Barr: like i've been doing in the session before - the default state is where i so SETUP
[12:45]  Fu Barr: *do
[12:45]  leman.resident has left the sim.
[12:45]  Fu Barr: in this case i take care of two events
[12:45]  Fu Barr: the enter the state event and the on rez event
[12:45]  Fu Barr: this is a nice time to talk about something new: the LIFECYCLE of a prim/script
[12:46]  Fu Barr: when a prim is rezzed, it fired the 'on_rez' event
[12:46]  Fu Barr: obvious...
[12:46]  Fu Barr: then after this happens it fires the state_entry() event of the default state
[12:46]  Fu Barr: bye!
[12:46]  Jeff Hall: see u Azi
[12:46]  Fu Barr: there's a very subtle difference.
[12:47]  Fu Barr: between on_rez and state_entry
[12:47]  Fu Barr: who wants to be the teacher's pet and tell me :)
[12:47]  Koni Lanzius: hehe
[12:47]  Fu Barr: jeff?
[12:47]  Fu Barr: wiz?
[12:47]  Jeff Hall: mm let me see
[12:47]  Fu Barr: pctek?
[12:48]  New Styler: raises hand
[12:48]  Jeff Hall: on rezz comes before
[12:48]  Fu Barr: okay - let's hear it new :)
[12:48]  Jeff Hall: i think
[12:48]  Jeff Hall: order of events
[12:48]  Fu Barr: yes it does jeff, you're right but that's not the complete and correct answer
[12:48]  New Styler: uhm on rez means do an action when object gets rezzed
[12:48]  Fu Barr: yes, but state_entry seems to do the same
[12:48]  Fu Barr: no?
[12:48]  New Styler: entry means action when its already rezzed
[12:49]  Fu Barr: excellent!
[12:49]  Fu Barr: so on_rez fires only ONCE
[12:49]  Fu Barr: when the pirm or linkset is rezzed into the world
[12:49]  New Styler: means does nothing on rez
[12:49]  Fu Barr: state_entry fires EVERYTIME you entre the default tstate
[12:49]  Fu Barr: so for example if you reset the script
[12:50]  Fu Barr: or if you're in aanother state and then more back to the default state
[12:50]  Fu Barr: so the life cycle of a prim/script is:
[12:50]  Lucy Afarensis: does this happen when sim restarts ?
[12:51]  Fu Barr: interesting question - I think yes. but i'm not sure. we'll have to check :)
[12:51]  Fu Barr: so state_entry and on_rez do very similar things, but they are different.
[12:51]  Tek Bachman has left the sim.
[12:51]  Fu Barr: okay
[12:51]  Fu Barr: on with the script
[12:51]  Fu Barr: 3 states
[12:51]  Object: Script running
[12:52]  Fu Barr: in the default state i set my latch to 'SEC_UNLOCKED'
[12:52]  Fu Barr: the reason for using CONSTANTS should be clear here....
[12:52]  Fu Barr: it makes it more readable...
[12:52]  Fu Barr: without need for orange comments
[12:53]  Fu Barr: the code is 'self-documenting'
[12:53]  Fu Barr: the i call llSetRot() - this sets the door to it's 'home' position
[12:53]  Fu Barr: ie 'closed'
[12:53]  Jeff Hall: please FU stop commenting on comments will you?...;)
[12:53]  Fu Barr: yes sir. wont do it again. ever.
[12:53]  Koni Lanzius: haha
[12:54]  Jeff Hall: is beginning to be repeatitive;)
[12:54]  Fu Barr: so when i;m done setting up my door - i move on to the closed state
[12:54]  Fu Barr: i know - but there are new people here so there's repetition
[12:54]  Fu Barr: i apologise
[12:54]  Fu Barr: but you;re right - i'll not say a word about it again
[12:54]  Koni Lanzius: no apology, there we new to this many of us
[12:55]  Koni Lanzius: we are new to this*
[12:55]  Fu Barr: so now that i've called 'stae closed' at the end of the default state... the script leaves the default state and enters the closed state.
[12:55]  Fu Barr: so now we're in 'state closed
[12:56]  Fu Barr: and of course... there we find the state_entry of the closed state.
[12:56]  Fu Barr: where i set up 2 listens
[12:57]  Fu Barr: and the reason for that is in the previous cessions on the wiki, but it boils down to me using the listen to filter the chat on the public channel 0
[12:57]  Fu Barr: then the closed state also handles the touch the door event
[12:57]  Fu Barr: if the door is locked - it tells the avatar - i;m locked
[12:58]  Fu Barr: else it opens the door
[12:58]  Fu Barr: and the flor owf the script goes to the 'state open'
[12:58]  Fu Barr: ow let's look at the 'listen' in the closed state
[12:58]  Fu Barr: the first if...
[12:59]  Fu Barr: if(DEBUG) etc.
[12:59]  Fu Barr: that we saw in the previous sessions... is a nice way of knowing what's going on in the variables in my script.
[12:59]  Fu Barr: more on that on the wiki logs.
[12:59]  Fu Barr: then we see i call the sec_acl_check() function
[12:59]  Fu Barr: in the second if()
[13:00]  Fu Barr: it means: if this avatar is on the ACL then let them say the magick words and lock the door
[13:00]  Fu Barr: the rest of the code should be self explanitory
[13:00]  Fu Barr: the only thing you need to be aware of...
[13:00]  Fu Barr: is that if(sec_acl_check(uuid))
[13:01]  Fu Barr: returns a TRUE/FALSE value
[13:01]  Fu Barr: which the IF needs to decide what to do.
[13:01]  Fu Barr: finally there's a state_exit() event handler
[13:01]  Fu Barr: which calls the door_swing() function and it swings the door.
[13:02]  Fu Barr: so the flow is:
[13:02]  Fu Barr: state default -> state closed.
[13:02]  Fu Barr: in state closed: we wait for a chat to locl/unlock the door
[13:02]  Fu Barr: or a touch the door to open/close the door
[13:02]  Fu Barr: . if the door is unlocked and there's a touch, more to state open
[13:03]  Fu Barr: else do nothing
[13:03]  Fu Barr: if the chat to the door is by somebody who is on the ACL - then accept the locking action, else do nothing
[13:03]  Fu Barr: :)
[13:03]  Fu Barr: now two final comments on this script
[13:04]  Fu Barr: 1. look how tiny the 'state open' is
[13:04]  Fu Barr: all it needs to do is close the door.
[13:04]  Fu Barr: all the 'logic' is in the 'state closed' part
[13:04]  Fu Barr: of course if you want to have a timed door, then you'd have to stick that stuff into the open state.
[13:05]  Fu Barr: the final comments is about the door_swing() function
[13:05]  Fu Barr: i;m not using the clossing 'rotate the prim through several degrees' method
[13:05]  Fu Barr: *classic
[13:06]  Fu Barr: i;m using the llSetKeyFramedMotion() function
[13:06]  Fu Barr: it's an AWESOME function for moving stuff around
[13:06]  Fu Barr: use it.
[13:06]  Fu Barr: read about it on the LSL wiki
[13:06]  Jeff Hall: is very smooth
[13:06]  Fu Barr: done ever do gegree calculations and stepped rotations ever again
[13:06]  Fu Barr: *degree
[13:07]  Fu Barr: this function takes care of all the heavy lifting
[13:07]  Fu Barr: again the wiki is your friend
[13:08]  Fu Barr: dassit for this script. it's really not so hard - please pull it apart and experiment with it.
[13:08]  Fae Peep: i have a question....
[13:08]  Fu Barr: like for example putting in the timed door in the 'open state'
[13:08]  Fu Barr: sure
[13:08]  Fae Peep: why do you need an attached prim for some door scripts?
[13:08]  Fae Peep: is it because they use the degree rotation?
[13:08]  Fu Barr: 'attached prim'?
[13:09]  Fae Peep: yes... i've found some doors like that in SL
[13:09]  posh prim has entered the sim. (79.78m)
[13:09]  Jeff Hall: linked i think he meant
[13:09]  Fae Peep: yes!
[13:09]  Fu Barr: oh you mean a door with more than one prim
[13:09]  Fae Peep: right
[13:09]  Fu Barr: with like a 'hinge' prim?
[13:09]  Fae Peep: right
[13:09]  Fu Barr: well that's because all rotations are done at the 'root'
[13:10]  Fu Barr: it's why old skool doors had that .375/0.875 cut
[13:10]  Fu Barr: if you rotaet a prim, it rotates on it's root axis
[13:10]  Fu Barr: *its
[13:10]  Fu Barr: whihc is in the middle of the prim
[13:10]  Fu Barr: and that's a weird door
[13:11]  Fu Barr: so you cut the half of the prim away - and hey presto a 'working' door
[13:11]  Fu Barr: the same idea with the 'hinge' prim
[13:11]  Fu Barr: if you make a cool coor with many prims
[13:11]  Fu Barr: *door
[13:11]  Fu Barr: you need to link it in such a way that the root prim of the link set is the one you rotate around
[13:12]  Fu Barr: am i explaining it ok?
[13:12]  Fae Peep: yes
[13:12]  Jeff Hall: instead rotating hte house:)
[13:12]  Fu Barr: let me rez the door and wall i made to show the v0.4 script in action
[13:12]  Fae Peep: the hinge prim... im remembering... yes i remember rotating a house or two
[13:12]  Fu Barr: one sec... need to find it in inv
[13:12]  Fae Peep: :)
[13:13]  Fu Barr: ther we go
[13:13]  Fu Barr: now look at theprims.... and yo'll see that my door rotates on the 'hinge' prim
[13:14]  Koni Lanzius: that is a beautiful door, Fu
[13:14]  Fae Peep: the gold hinge?
[13:14]  Fu Barr: meh- it's quick and dirty - and the scrip isn;t fool proof - but it's fine for the educatinoal purpose
[13:14]  Fae Peep: so the scripty is in there? in the gold hinge prim?
[13:14]  Fu Barr: yep :)
[13:15]  Fae Peep: i see
[13:15]  Fu Barr: in this case it's all about the root prim
[13:15]  Jeff Hall: nice door fu
[13:15]  Fu Barr: right - anymore questions - if not we'll say a few words about lists
[13:15]  Koni Lanzius: ~** APPLAUSE **~
[13:15]  Fu Barr: :)
[13:15]  Fu Barr: so...
[13:15]  jerrys avgoustis is Offline
[13:16]  Fu Barr: like jeff mentioned
[13:16]  Fu Barr: at the very beginning of the session
[13:16]  Fu Barr: we havent spoken about llGetObjectDesc and other methods of data persistence yet
[13:17]  Fu Barr: :)
[13:17]  Fu Barr: so back to lists
[13:17]  Fu Barr: a list is simply that
[13:17]  Fu Barr: a list
[13:17]  Fu Barr: so weve seen strings
[13:17]  Fu Barr: juts words
[13:17]  Fu Barr: and letters
[13:17]  Fu Barr: and integers kust numbers like 4 5 6 7 8
[13:17]  Fu Barr: and then there's floats
[13:17]  Fu Barr: 3.14 or 4.56678
[13:18]  Fu Barr: something with a 'dot' in them
[13:18]  Fu Barr: and then there's lists
[13:18]  Jeff Hall: lists yes
[13:18]  Fu Barr: lists are 'lists' of string or integers or floats or for that matter any other type of variable LSL knows about
[13:18]  Jeff Hall: very powerful
[13:18]  Fu Barr: so an example of a list:
[13:19]  Fu Barr: list my_list =  [1, 2, 3, 4, 5]
[13:19]  Fu Barr: this measn that the list named 'my_list' has 5 'members' and they run from 1 to 5.
[13:19]  Fu Barr: or this list:
[13:19]  Fu Barr: list my_list =  ["abc", 1, <0,1,2>, 4.5567]
[13:20]  Fu Barr: this is a list with 4 members
[13:20]  Fu Barr: the first is a string
[13:20]  Fu Barr: the second is an integer
[13:20]  Fu Barr: then 3rd is a vector
[13:20]  Fu Barr: and the 4th is a float
[13:20]  Fu Barr: all nicely grouped together in a single list
[13:20]  Fu Barr: now the question is.... why do i need this?
[13:21]  Fu Barr: well to answer that, lets go back to the v0.4 script
[13:21]  stiofain nbmcmedia is Online
[13:21]  Fu Barr: i need someplace to kepe the list of avatars who can lock/unlock the door
[13:21]  Fu Barr: so i made a list
[13:21]  Jeff Hall: this is the lovely part
[13:22]  Fu Barr: and then my list checking thing becomes really easy
[13:22]  Fu Barr: all i need to do to check if somebody is allowed to use the lock, is to check if they are on my 'list'
[13:22]  Fu Barr: like so:
[13:22]  Fu Barr: llListFindList(sec_acl,  [llKey2Name(uuid)]
[13:23]  Fu Barr: i feed the uuid of the avatar into the key2name function
[13:23]  Fu Barr: the result of that conversion to a 'name bla" is then searched for in the sec_acl list.
[13:23]  PcTek CyberStar: im not sure why it's acting like that
[13:24]  Fu Barr: and if it is found - well then boom! then that avatar can use the latch
[13:24]  Fu Barr: else.... not.
[13:24]  Fu Barr: so listts are really important in any sort of more complicated script - they allow you to group a bunch of values together and that helps with  keeping your data organised.
[13:25]  Fu Barr: the wiki has a whole bunch of list manipulation functions
[13:25]  Fu Barr: the LSL wiki that is
[13:25]  Fu Barr: adding to a list, slicing a part of the list, counting a list
[13:25]  Fu Barr: all sort of fun stuff
[13:25]  Lucy Afarensis: Is the list a separate text file ?
[13:26]  Fu Barr: nope
[13:26]  Fu Barr: it's a 'shopping list in the servers memory'
[13:26]  Fu Barr: you can write the list to a file or a notecard, and you can read it back... but it's in memory while the script runs :)
[13:26]  Jeff Hall: u can build a list from a notecard Lucy
[13:27]  PcTek CyberStar: see look now fae
[13:27]  Fu Barr: anymore questions about the 'list'
[13:27]  Fu Barr: it's really just like a digital egg-carton
[13:27]  Fu Barr: just not 6 or 10 or 12... but 'unlimited'
[13:28]  Fu Barr: till you run out of script memory
[13:28]  Fu Barr: but that's relatively big for your script needs
[13:28]  abaddon Debevec has entered the sim. (111.15m)
[13:28]  Fu Barr: okay - well that brings us to 90 minutes again :(
[13:28]  Fae Peep: is there a group uuid? ... just wondering
[13:28]  Fu Barr: i hope it was clear and useful. please review the chat logs on the wiki
[13:28]  Fae Peep: for group entries
[13:29]  Fu Barr: so we wont have to repeat so much