Friday, 23 June 2017

C# String Format in LSL

Usually at work I use C#. There is a method in the string class of C# called Format. This little beauty of a method replaces any {0}, {1}, {2}, etc with the 1st, 2nd, 3rd parameters of that method. For example String.Format("My name is {0} and I love {1}", "SighFye", "C#"). This will come out as "My name is SighFye and I love C#". This is a method I use a lot in C# and when scripting for Second Life I find the lack of the method a little disappointing. So I decided to create it. Below is a copy of my method that does the same as the C# String.Format.

In my version instead of having dynamic params I use a list variable. So lets test it out. I have added this to my script.

And my results are...
So there it is the C# String.Format method in LSL. Hopefully this can help take you code to a new level.



Wednesday, 6 July 2016

Call of Duty Log File Deciphered

Recently I have been building a new style of Rcon for Call of Duty servers. One the required things is to read the log file to determine who killed who. Searching the net I was unable to find an explanation of the log file meanings, so bit by bit I have figured it out and wanted to share my knowledge.

Most lines are separated into parts using a semicolon ";". The only exception to this is the InitGame line. These are separated with a backslash "\". The first part of each line consists of a timestamp and a log code to determine what is happening. Lets look at each of the codes. Anything underlined, I am unsure of.


1467788346 ------------------------------------------------------------
This line I believe is to make the log file more readable. It comes at the beginning and end of each game.

1467788346 InitGame: \com_maxclients\19\g_gametype\... etc etc
This line contains all the details of the server for this game. It comes up twice. The second time showing one extra part "g_logTimeStampInSeconds\1". To be honest I do not know why its twice.

1467800872 ExitLevel: executed
1467800873 ShutdownGame:

These are when the game ends.

1467790500 J;49289532;5;smizajb
1467790501 Q;16065554;8;CuBoy
This is when someone Joins or Quits the server.
[timestamp][code][Player Guid][Server Slot][Name]

1467790518 K;58546612;3;axis;44 Mag;130326688;4;allies;BoW- b0yb0y07;kiparis_grip_extclip_mp;30;MOD_RIFLE_BULLET;torso_upper
This is a kill
[timestamp][Code][Dead Players Guid][Dead Players Server Slot[Dead Players Team][Dead Players Name][Attacker Guid][Attacker Server Slot][Attacker Team][Attacker Name][Gun Used][Damage][The thing that killed the player][Where that thing hit]

1467790520 D;69633548;1;allies;clarkymagpie;73534678;7;axis;WSPh3A;spectre_rf_mp;24;MOD_PISTOL_BULLET;torso_lower
This is damage, could be a death as well, but does not count towards any players kills. For example, this is used when a player falls off the map.
[timestamp][Code][Damaged Player Guid][Damaged Players Server Slot][Damaged Players Team][Damaged Players Name][Attacker Guid][Attacker Server Slot][Attacker Team][Attacker Name][Gun Used][Damage][The thing that damaged the player][Where that thing hit]

1467790537 Weapon;105885919;10;iFinishInside;spectre_rf_mp
This one I am unsure of. I think it's when the player picks up a weapon or reloads their weapon.
[timestamp][code][Player Guid][Player Slot][Player Name][Weapon Picked Up or Reloaded] 
My guess being picked up a weapon is this line
1467790705 Weapon;191086122;8;The Dan-kest;scavenger_item_mp 
By the looks of it, the player picked up some ammo using scavenger perk. My guess being reloading is these lines.
1467785832 Weapon;156430714;2;SniperSkillz;famas_dualclip_mp
1467785833 Weapon;156430714;2;SniperSkillz;famas_dualclip_mp
1467785834 Weapon;156430714;2;SniperSkillz;famas_dualclip_mp
1467785834 Weapon;156430714;2;SniperSkillz;famas_dualclip_mp
These are all done in a row in my log file. There's like 20 of them. I would assume there couldn't be that much ammo laying on the ground, so my guess is this player is playing with their ammo clip. Starting the reload process and cancelling it.

1467792113 say;64153711;1;FC.BellSigh; gg
1467800021 sayteam;59516882;3;r$k_nz; sry
This is when someone says something.
[timestamp][Code][Player Guid][Player Slot][Player Name][What They Said]

1467800169 VD;266;;7473421;5;axis;Special Forces P;commando_extclip_silencer_mp;30;MOD_RIFLE_BULLET;none
This one I haven't figure out just yet. If you know it please comment below.

So that's it guys and girls. I hope this helps. If you know of any things I have gotten wrong then please comment below and I will update them.

Saturday, 23 January 2016

Poker Hand Analyser

In this post I will be showing a poker hand analyser. It will be able to be used in either 5 Card Poker or Texas Holdem. In this algorithm we are not checking all players hands, only one hand of 5 or 7 cards. It can be reused for each players hand. Let's start by first solving the problem, before we look at working on what the hand contains.

In poker there are ten possible hands. In order from best to worst they are;
  1. Royal Flush
  2. Straight Flush
  3. Four of a Kind
  4. Full House
  5. Flush
  6. Straight
  7. Three of a Kind
  8. Two Pair
  9. One Pair
  10. High card
 There are a few things to look at before writing a poker hand analyser.

If you have a straight or a flush, you CAN NOT have a full house or four of a kind.
This is because having a straight or a flush takes five of the cards. In five card poker that's all your cards, and in texas hold'em you are left with only two cards to use. Its not enough to make anything else.

In Texas Hold'em, you CAN have a flush AND a straight WITHOUT it being a straight flush
Imagine you have a straight with 3 hearts and 2 clubs, then you have 2 more hearts no where near the straight. In this case you have both a straight and a flush. Since flush is better, the flush has to be used.

Ace must be used as both high AND low.
In a straight the ace has to be tested for low high and low straights.

In Texas Holdem, a players hand is 7 cards in total. Two in their hand and five on the table.

So let's get started. Below is a flow chart of the algorithm. Using this will enable us to test for the best hands first, and then moving through to the lowest hands without over checking. For example, if we don't have a flush there's no need to check for straight flush or royal flush.



Checking For A Flush

So let's say you were dealt this hand.

And out on the table we have.

The first thing we need to do is sort them into suits. Create four arrays named diamonds, hearts, clubs and spades. Next loop through each card and check the suit using a select case statement. In the case for each suit place the card in the relevant array.

Next all you need to do is check the length of each array. If its five or more then we have a flush. You can then return the flush array for further processing.

Checking For A Straight

Checking for a straight is a little more complex so we will go through this step by step. Let's say we have been dealt this hand.

And the table has.

First of all we need to sort the cards. This can be done with a bubble sort using the card value. In my example Ace has a card value of 14.


We already can see we have a straight using A, 2, 3, 4 & 5. Now before we loop through the cards we need a few variables for;
  1. Keeping count of the straight length (tempCount) preset to 1
  2. Keeping track of the start of the straight (startIndex) preset to -1
  3. Keeping track of the end of the straight (endIndex) preset to -1
  4. The lowest value card (lowestValue) preset to null
  5. Whether the ace is included or not. (aceIncluded) preset to false
So with those set up we can now loop through the cards backwards. Start with the highest value card and loop to the lowest. First thing you want to do is check if the card is an ace so we can use it as a low card too. In our example it is an ace, so we set aceInclude to true.

Next we check if the value of the card in the current loop is 1 greater in value than the card in the next loop.
This is true so we check if endIndex equals -1 and if so set endIndex to the current loops index. In this example 6. Add 1 to the tempCount which makes it 2 saying there are 2 cards in the straight, and the startIndex we set to this index - 1. In the example 5. And lastly set lowestValue to the value of the card in index - 1 (The King) which is 13.

Then we do the same again for the next card.
This one is false. So we first check if the card is the same value. King = 13 and 5 = 5 so they are not the same value (We'll see why we do this when we get to the two 4's. Now just check our count. If it already equals 5 we can break the loop. Otherwise we reset startIndex  endIndex to -1 since we no longer have a straight potential. Set tempCount back to 1 and the lowestValue card to the value of the card in index - 1.

So let's continue a bit and do the next one.
Once again we are true so;
  1. endIndex = current index = 4
  2. startIndex = current index - 1 = 3
  3. tempCount increases by 1 = 2
  4. lowestValue = current index - 1's value = 4
 Now the next step.

This time we are false, however both cards are of equal value. If we reset all the values we will loose our straight, and we can see we have one. However the computer can not see the cards ahead so in this case, we include the card in the straight but do not add to the tempCount.
  1. startIndex = currentIndex - 1 =3
 To include it all we do is move the startIndex to that card. Because we haven't added 1 to the tempCount the computer still sees 2 cards to make up a straight but we have three cards to use. In the end we will only use one of the 4's

Checking the rest of the cards has already been explained so let's just rush through that.
Once again we are true so;
  1. endIndex does not equal -1 so we leave it alone
  2. startIndex = current index - 1 =1
  3. tempCount increases by 1 = 3
  4. lowestValue = current index - 1's value = 3
Now the last step in the cards
Once again we are true so;
  1. endIndex does not equal -1 so we leave it alone
  2. startIndex = current index - 1 =0
  3. tempCount increases by 1 = 4
  4. lowestValue = current index - 1's value = 2
So now we have looped through all the cards and counted our straight. To test if the straight is present all we need to do is check our tempCount. If the tempCount is equal to or greater than 5 than we have a straight. We can go ahead and use the startIndex and endIndex to create a new array containing the best hand for further procressing.

Now in our case we know we have a straight but our tempCount only equals 4. This is because the Ace is a part of our straight but it seen as a high card. So, if testing for 5 or more in the straight fails, test for 4 in the straight. If tempCount is equal to 4 we need to check the lowestValue. If the lowest value equals 2 in our straight of 4 then we know we have 2, 3, 4, 5. All we need is an ace to complete the straight. We already checked for an ace when we looped the cards the first time, so, if aceIncluded is equal to true, AND tempCount is equal to 4 AND lowestValue is equal to 2 then we have a straight. Use startIndex and endIndex to get the first part of the straight and then get the card in the last index. Return these as your best hand for further processing.

Prepare For Four of a Kind, Three of a Kind, Full House and Pair Checks

It 's best to have the cards sorted by value before starting this.
Since check for these are all the same process, I decided to do it all in one sweep. We will do this similar to the flush with a few arrays. First start by making seven arrays. Since we have seven cards there is a chance to get seven different value cards. Lets call these temp1 through to temp7.

First we loop through each card checking it's value. Let's take our last hand as the example.

We can see we have a pair of 4's. Since we don't have set arrays for the card type we need to loop through each card check its value and then find either an array with the same value cards or an empty array. So let's start with the 2. First check the length of temp1. It's empty so it returns 0. We know the first array is empty so all the others will be too. Place the 2 in that array.

The next card is a three. First check temp1's length. This returns 1 so there's a card in there. Then check that cards value, which is 2. Its not the same as our card's value so we move to the next array. This one is empty so we can just put this card in.
Continue this for the next step.
Now this next card when checking the card values will match the card in temp3, so put it in the same array.
The rest of the cards won't match any arrays so after all the cards have been sorted will look like this.

Checking For Four of a Kind

To check for a four of a kind, simply loop through the arrays temp1 through to temp7 and see if any of them have a length of 4. If you find one, take that array and one card from the highest value array other then the array you are using, and return it as your best hand for further processing.

Checking For Three of a Kind AND Full House

To check for a three of a kind you can do the same process as with four of a kind, HOWEVER, in texas holdem, you have the potential for 2 sets of three. So, if you find a three of a kind you need to continue to see if you find another. If you do find another, the highest value set plus two cards from the lowest make up your best hand. If only one set of three is found it is taken as best hand along with two cards from the highest value other than the array you are using.

When you find only one three of a kind, you need to see if it's a full house. You need to then loop through and check for a pair. If you find a pair, check for a higher pair. Take the highest pair with the three of a kind and return it as best hand for further processing.

Checking For A Pair Or Two Pair

To check for a pair, loop through the arrays looking for pairs. There is a potential for three sets of pair so make sure you find the highest pairs. If you find 1 pair you have a pair, take that with the 3 highest value cards not including the pair and you have your best hand. If you find 2 pairs, take them and the highest value card not including your two pairs and you have your best hand. If you find 3 pairs, take the two best pairs along with the highest value card other than the pairs you are using and return it for your best hand.

Putting It All Together

So all of these things above can be placed in functions. You hand your 5 or 7 cards to the function and it either returns an empty array or an array of cards. Now all you need to do is may a anaylse hand function which follows the same process as the flow chart we made...
1. Is there a flush? To check what the function results are we check the length of the return array. If it is greater than 0 then we have a flush. Move to step 1 a. If its 0 move to step 2.
    1a. Do we have a straight? Once again check the length of the returned array. If its greater than 0 then move to step 1. a i. Otherwise move to step 1. b)
        1ai) Do we both the King and The Ace. To test for this loop through your returned cards and check for a value of 13 AND 14. If you find them both you have a ROYAL FLUSH. If not you have a STRAIGHT FLUSH.
    1b. If we don't have a straight then we have a FLUSH.
2. Do we have a straight? Once again check the length of the returned array. If its greater than 0 then we have a STRAIGHT. If it is 0 then we move to step 3.
3. Sort out the cards into value arrays. Go to step 4
4. Do we have Four of a Kind? If yes, FOUR OF A KIND, if not go to Step 5
5. Do we have Three of a Kind? If yes, go to step 5a. If not goto step 6.
     5a. Do we have a full house? If yes, FULL HOUSE if no THREE OF A KIND
6. Do we have 1 pair? If yes Go to Step 6a. If not go to Step 7
    6a. Do we have another pair? If yes,  TWO PAIR, if not PAIR
7. Nothing else is possible so you have HIGH CARD. Take the highest value cards from the hand as your best hand.

This algorithm can be streamlined heaps by doing things like breaking from loops if there ain't enough cards to complete what you are looking for. For example, if you are looking for a straight, and have 2 card already making up a straight but have only 2 cards left, there is no point checking them. You can not make a straight. Unless of course you have 5, 4 and an Ace. You may have 3 and 2 in the last one. But I'll leave it up to you to streamline.

I hope this helps with creating your poker app. If you need help writing it in a set language I am happy to write a post with that and link it to the bottom of this post. Just ask in the comments.

Friday, 18 December 2015

Moon Lander: Movement Tutorial

This tutorial is a basic game of physics. The player controls a rocket ship's rotation and thrust. With limited fuel and the effects of gravity, the player must try to land the rocket safely. Hit the ground too hard, and your rocket explodes.

In this tutorial, if code is crossed out like this...

if code == "CROSSED OUT" then
    print "Add this line of code"

...it means this line of code will already be there. You only need to add the code in bold. In this part of the tutorial we will be going into the movement of the rocket and the gravity effect. Let start by grabbing a copy of my Corona Template.

The first thing we need of course is our rocket ship. I personally found this little ship on google. Find yourself a little rocket like mine and place it in the sprites folder. Now we need to get that rocket on the screen. To do this we are going to make ourselves a function to create a new player. Add the following code just before the scene:enterScene function

function newPlayer(x, y)
    local player = display.newGroup()
    player.x                 = x
    player.y                 = y
    player.image             = display.newImage("sprites/rocket.png")
    player:insert(player.image) --Inserts the image into the display group
    return player

end

How this works
  1. Create a new display group within the function
  2. Move that display group to the x, y coordinates given to the function
  3. Create a new image from the file we have given.
  4. Place that image inside the display group
  5. Returns the display group
The reason I created a display group first is I can easily add more images or variables to the object created. Later we will need to add some variables to this, but lets first get this rocket on the screen first. To create a rocket on your screen all you now need to do is call the function you just created. Place this next line of code inside the scene:enterScene function, just after the mainScene = self.view line

mainScene = self.view
--Create a player
player = newPlayer(DISPLAY_W_CENTER, DISPLAY_H_CENTER)
mainScene:insert(player) 

So as you can see we are creating a new player using the constants pre-made in the template. These constants are for the center of the screen. We are then storing the player in a variable called player.
This should create our rocket on the screen.

Image of Corona Simulator With Rockey On Screen

The next step is to work on the rotation of the rocket. The first thing we need for that is a couple of buttons to press. One for rotation left and one for rotating right. I found these great images on google. Find some images of your own and then to add them to the screen add the follow code after the main function



----------------------------------------------------------------
-- MAIN LOOP
----------------------------------------------------------------

local function main( event )

end

--Buttons
btnRotateLeft = display.newImage("sprites/object_rotate_left.png")
        btnRotateLeft.y = 640


btnRotateRight = display.newImage("sprites/object_rotate_right.png")
        btnRotateRight.x = btnRotateLeft.width * 1.5
        btnRotateRight.y = 640

mainScene:insert(btnRotateRight)
mainScene:insert(btnRotateLeft)



Okay now if we touch these images, nothing happens. We need to add event listeners to them. Add the following code under the buttons.

btnRotateLeft:addEventListener("touch", player.turn)

and

btnRotateRight:addEventListener("touch", player.turn)

What these lines of code are doing is telling the program to look for a touch event on the button. If a touch event is found, it will run the player.turn function. However if you run this now, you will get an error because we do not have a function turn in the player object. So let's create this function now. In the newPlayer function add the following code.

player.turn = function(event)
                 if event.phase == "began" then
                                       
                 end
              end


We now have our function, however both buttons call this function so we need some way to check which button was pressed. To do this we add the following code just below the buttons.

btnRotateLeft.dir = "LEFT"

and

btnRotateRight.dir = "RIGHT"

So your button code should look similar to this.

btnRotateLeft = display.newImage("sprites/object_rotate_left.png")
        btnRotateLeft.y = 640
        btnRotateLeft.dir = "LEFT"
        btnRotateLeft:addEventListener("touch", player.turn)
 btnRotateRight = display.newImage("sprites/object_rotate_right.png")
        btnRotateRight.x = btnRotateLeft.width * 1.5
        btnRotateRight.y = 640
        btnRotateRight.dir = "RIGHT"
        btnRotateRight:addEventListener("touch", player.turn)

mainScene:insert(btnRotateRight)
mainScene:insert(btnRotateLeft)


Now all we need to do is check in the player.turn function which button was pressed. Inside the event variable there is heaps of information about the event. As you can see we are using phase so the function only runs when the button is first pressed and not again when its released. Another piece of information is target. The target is the button pressed in our case. Each of our buttons have a dir variable so now we can check which button was pressed using event.target.dir. Add the following code straight after the event.phase check.

if event.phase == "began" then
    if event.target.dir == "LEFT" then
                               
    else
                                       
    end


Now all we need to do is rotation the ship left or right. Add the following code.

if event.phase == "began" then
    if event.target.dir == "LEFT" then

        player.rotation = player.rotation - 2                       
    else
       
player.rotation = player.rotation + 2                            
    end


Now, if we run our code and touch the buttons, you will see the ship is now rotating. However its not quiet centered. Add this code to the player function.

player.image.x         = player.image.x - player.image.width/2
player.image.y         = player.image.y - player.image.height/2


This will move the image so when the ship rotates, it rotates on it's center. Another annoying thing is it's only moving 2 degrees every click. It would be much better if while we were holding the button it would continuously rotate. So lets change our code to implement this.

The first thing we need to do is give the player another variable. Add the following code to the player function.

player.rotating = {false, nil}

This line enables us to do a check to see if the ship should be rotating and by how much. Now in the turn function change

player.rotation = player.rotation - 2

to

player.rotating = {true, -2}

and

player.rotation = player.rotation + 2

to

player.rotating = {true, 2}

Now add and else on the event phase check.

elseif event.phase == "ended" then
    player.rotating        = {false, nil}end

Your full player.turn function should look like this.

player.turn= function(event)
               if event.phase == "began" then
                  local direction = event.target.dir
                  if direction == "LEFT" then
                      player.rotating = {true, -2}
                  else
                      player.rotating = {true, 2}
                  end
               elseif event.phase == "ended" then
                  player.rotating        = {false, nil}
              
end
                            end

The last part of the rotation is to do a check to see if the ship is rotating. This is done in the main loop. Add the following code in the main loop

--Rotate Player
if player.rotating[1] then
    player.rotation = player.rotation + player.rotating[2]
end


Now when we click and hold on our buttons. The ship rotates continuously. We are half way there. Now we need to add the movement. First lets limit the ships rotation so it can not turn upside down. Add the following lines of code after we do the rotation in the main loop.

if player.rotating[1] then
     player.rotation = player.rotation + player.rotating[2]

     if player.rotation < 270 and player.rotation > 180 then player.rotation = 270 end
     if player.rotation > 90 and player.rotation < 180 then player.rotation = 90 end

And finally, so our rotation value does not go into a negative number add these line of code right under the ones you just wrote.

if player.rotation <= -1 then player.rotation = player.rotation + 360 end
if player.rotation >= 360 then player.rotation = player.rotation - 360 end 

With these lines of code, if the rotation drops below 0 we add 360 to the rotation to get a true value. Likewise when it raises higher then or equal to 360 we then remove 360.

Now lets get onto thrust by adding a thrust button. Once again I found a cool button on Google. You can find whatever button you wish to use. This button is way to big to use on the screen but it does give me a chance to show how to scale the size of a button.
It is done by using the scale function.

imageName:scale(x,y)

I'll be using this function when I scale this image in code. Lets go ahead and add this button to our screen. Add the following code where you made the rotating buttons.



btnBoosters = display.newImage("sprites/Boosters.png")
        btnBoosters:scale(.5, .5)
        btnBoosters.y     = 640
        btnBoosters.x     = 1000
        btnBoosters:addEventListener("touch", player.
fireThruster)

So like before I have added an event listener so when the player touches the image, the function player.startEngine runs. You can also see I have scaled the image to half the x and half the y. Running the code now will cause an error as there is no player.startEngine function. So lets get that one made now. Add the following code inside the newPlayer function.

player.fireThruster = function(event)
                        if event.phase == "began" then
                                   
                        elseif event.phase == "ended" then
                                   
                        end
                     end


Now we can run our code and see our button. Here is what is should all look like now.


Okay so lets start our engines. In the fireThrusters function all I will be doing is adding some value to a new variable we are about to make. In the newPlayer function add this line of code.

player.thrust = 0

And now in the fireThrusters function add the following code.


player.fireThruster = function(event)
                        if event.phase == "began" then
                            player.thrust = 0.5    
                        elseif event.phase == "ended" then
                            player.thrust = 0     
                        end
                     end


Now when we touch the button, our new thrust variable will get a value of 0.5 and when we release our touch it will drop back to 0. Now to add some movement. Add the following variable to newPlayer.

player.velocity = {x=0, y=0}

This will be used to determine how far to move our ship on the x and y axis. We will work on this section one direction at a time. Start by adding a move function to the newPlayer function.

player.move = function()

              end

And in the main loop, just after we make our ship rotate, call this new function.

local function main( event )
       
         --Rotate Player
    if player.rotating[1] then      

                ....
                ....
    end
     --Move the Player based on thrust
     player.move()



Okay now we need to make the ship move. There are a few sections to this. Lets start by making the ship move when facing straight up. In the move function add the following code.

local angle = player.rotation
if angle == 0 then --Ship Facing Up
    player.velocity.y = player.velocity.y - player.thrust

end

When the ship is facing up, we only need to move on the y axis. To do this we simply take the thrust from the y axis velocity. This will make the ship move up the screen when the velocity is applied. To make the ship move add the follow code after what you just entered.

player.y = player.y + player.velocity.y
player.x = player.x + player.velocity.x


These two lines will move the ship based on the velocity. If you run your code now your ship should fly upwards when you hit thrust. However, if you turn the ship, the thrust button will now work. We will add these a little later. Now lets work on when the ship is laying on its left side. In the angle check add the following elseif statement.

if angle == 0 then -- Straight line
      player.velocity.y = player.velocity.y - player.thrust

elseif angle == 270 then-- Straight line
      player.velocity.x = player.velocity.x - player.thrust

 end


This is almost identical to when facing straight up except we need to take the thrust from the x velocity making the ship move left. Save your code and try it now. You should be able to turn your ship left and it will stop when it is perfectly laid down and thrust. Now lets add this movement when the ship is laid on the right side. Add the follow code, also to the angle check.

if angle == 0 then -- Straight line
      player.velocity.y = player.velocity.y - player.thrust

elseif angle == 270 then-- Straight line
      player.velocity.x = player.velocity.x - player.thrust

elseif angle == 90 then-- Straight line
      player.velocity.x = player.velocity.x + player.thrust

 end


Now if you test your code the ship should fly when laid on its right side. Okay now we move into the more tricky movements. First lets do when the ship is leaning to the left. First lets add that elseif statement in the angel check. This check need to see if the angle is greater than 270.


if angle == 0 then -- Straight line
      player.velocity.y = player.velocity.y - player.thrust

elseif angle == 270 then-- Straight line
      player.velocity.x = player.velocity.x - player.thrust

elseif angle == 90 then-- Straight line
      player.velocity.x = player.velocity.x + player.thrust

elseif angle > 270 then
 
 end


For this one we need to work out what percentage of that 90 degree section the ship is leaning. Lets for example say the rotation of the ship is 296 degrees.
We need to first find out what the angle is just in that quarter of the angle. So if we take away 270 from the angle we will have the specific angle we need. In our example our angle to work with would be.

296 - 270 = 26 degrees
  
Now we need to work out what percentage of the 90 degrees the ship has turned through.

26 / 90 =  0.29

The angle we measure has come from the x axis so this number is worked out to find how much thrust to put on the y axis. To get the x axis thrust we simply take this number from 1.

1 - 0.29 = 0.71

 To implement this is code add the following lines to the new elseif statement.

elseif angle > 270 then
    angle = angle - 270
    local yPercent = angle / 90
    local xPercent = 1-yPercent

 end 



Now to place the thrust on the x and y velocity we simply add this code.

elseif angle > 270 then
    angle = angle - 270
    local yPercent = angle / 90
    local xPercent = 1-yPercent

    player.velocity.x = player.velocity.x - (player.thrust * xPercent)
    player.velocity.y = player.velocity.y - (player.thrust * yPercent)

 end 


Save and run your code and you will see you can now fly leaning left. Now lets follow the same procedure and add the ship flying to the right. The difference is because the angle is now measured from the y axis, the first part measures the x axis thrust. Also because the angle is already under 90 we do not have to adjust it. Add the following else statement to your angle checks.

else
    local xPercent = angle / 90
    local yPercent = 1-yPercent

    player.velocity.x = player.velocity.x - (player.thrust * xPercent)
    player.velocity.y = player.velocity.y - (player.thrust * yPercent)
 end



Now your ship should fly anyway. However, we don't have any gravity to bring our ship back down. So lets put that in now. Simply add this line right after the if statements we have just been writing and before we add the velocity to the x and y coordinates.

player.velocity.y = player.velocity.y + 0.1

Now your ship should fly up, left and right while being effected by gravity. Now only one more thing to implement. Lets stop our ship from flying off the screen and zero the velocity when it hits the side. Add the following lines of code after we add the velocity to the x and y coordinates.

--Stop the ship flying off the screen
if player.x < 0 + player.width then
       player.x = 1 + player.width
       player.velocity.x = 0
elseif player.x > DISPLAY_W - player.width then
       player.x = DISPLAY_W - player.width
       player.velocity.x = 0
end
if player.y < 0 + player.width then
       player.y = 1 + player.width
       player.velocity.y = 0
elseif player.y > DISPLAY_H - player.width then
       player.y = DISPLAY_H - player.width
       player.velocity.y = 0
end


Now our ship won't fly off the screen.

Well that's it for now. Keep an eye out for the next tutorial for the moon lander game. Next time we will make the ship crash if we land too hard and maybe add some fuel restrictions.

You can download the code and all the images right here.

Thursday, 17 December 2015

Mobile App Template For Corona

I've written a few apps in my time coding. Only one of them, Dead Run, has made it to the Google Play store. One thing I do keep in common, is I use the Corona SDK. Corona is a cross platform mobile app development software. You write the code once, Corona converts it for each platform.

One thing that I find makes me procrastinate in starting a new app is all the setup. Okay it's not really much but its still all the boring shit I don't really want to be doing. I want to create my app. So recently, I decided to make myself a template. This mean if I'm in the mood to start an app I can just pop in and start building.

The first file a corona app needs is the build settings. Here is my template build.settings file.

build.settings


settings =
{
  orientation =
   {
    default = "landscapeRight",
    supported = {"landscapeRight"}
   },
  
   androidPermissions =
   {
       "android.permission.WRITE_EXTERNAL_STORAGE",
       "android.permission.INTERNET",
       "android.permission.READ_PHONE_STATE",
       "android.permission.ACCESS_NETWORK_STATE",
   },
   android =
   {
        usesPermissions =
        {
            "android.permission.INTERNET",
            "com.android.vending.BILLING",
            "com.android.vending.CHECK_LICENSE"
        },
   },
   plugins =
    {
        ["plugin.google.iap.v3"] =
        {
            publisherId = "com.coronalabs",
            supportedPlatforms = { android=true }
        },
    }
}


This has all the basics I will need for my apps. If I find my app will need other settings I can easily return and add them in or even take some out later.

The next required file is..

config.lua


application =
{
    showRuntimeErrors = true,
   
    content =
    {
        graphicsCompatibility = 1,
        width = 720,
        height = 1080,
        scale = "zoomStretch",
        xAlign = "center",
        yAlign = "center"
    },
    -- license =
    -- {
        -- google =
        -- {
            -- key = "",
        -- },
    -- }

}


These two are probably the main two require file but I like to also set myself up a little more with two more files.

main.lua


system.activate( "multitouch" ) --Activate the multi-touch
display.setStatusBar( display.HiddenStatusBar )

local storyboard = require "storyboard"
storyboard.gotoScene("game"


And finally since I'm using storyboard and telling it to go to game.lua, I need to create that one too.

game.lua

local storyboard = require "storyboard"
local json = require ("json")
local physics = require("physics")
physics.start()
--physics.setDrawMode("hybrid")
local scene = storyboard.newScene()
storyboard.purgeOnSceneChange = true

--This function prevent or alters the phones back button function

local function onKeyEvent( event )
    local keyName = event.keyName
    local phase = event.phase

    if (keyName == "back" and phase == "up") then

        --Put in something else for back button
        return true
    end
    return false
end

--constants

DISPLAY_W = display.contentWidth
DISPLAY_H = display.contentHeight
print("Display="..DISPLAY_W..":"..DISPLAY_H)
DISPLAY_W_CENTER = DISPLAY_W/2
DISPLAY_H_CENTER = DISPLAY_H/2
--Sprites
-- local playerSheetData = { width=163, height=151, numFrames=9, sheetContentWidth=489, sheetContentHeight=453 }
-- local playerSheet = graphics.newImageSheet( "sprites/player.png", playerSheetData )
-- local playerSequenceData =
-- {
    -- {name = "stand",     start = 2,    count = 1},
    -- {name = "walk",     start = 1,    count = 4, time = 500},
    -- {name = "fight",     start = 5,     count = 3, time = 200},
    -- {name = "die",         start = 8,     count = 2, time = 800, loopCount=1}
-- }

--This function runs when the user touches the screen

function performAction(event)
    print(event.phase)
    if event.phase == "began" then
    end
    return true
end


-------------------------------------------

----------- ENTER SCENE EVENT  ------------
-------------------------------------------
function scene:enterScene( event )
    ----------------------------------------------------------------
    -- MAIN LOOP
    ----------------------------------------------------------------
    local function main( event )
       
    end
    --Event Listeners
    Runtime:addEventListener( "touch", performAction ) --User Touches The Screen
    Runtime:addEventListener( "enterFrame", main) -- Enter Loop
    Runtime:addEventListener("key", onKeyEvent) -- User Presses a phone key
    --Runs when you destroy the scene, great for cleanup
    function scene:destroyScene(e)
        Runtime._functionListeners = nil
    end
    --scene:addEventListener("destroyScene",scene)
end

scene:addEventListener("enterScene", scene )

return scene

I place all of these in a folder called Source Code. Inside Source Code I also place a folder called sounds and another called sprites. Then whenever I want to start a new app, I just copy and paste the template folder and bang, I'm building straight away.

I hope this helps you get through those procrastination moments in the same way it helps me.

You can download the template here.