Author Topic: Captain Basilbeard's Unoffical Item Creation Guide(Part 2 is out)  (Read 20948 times)

Offline Basil Beard

After reading tazpot's post Here I decided to take him up on it.  :P. After all, item creation is pretty easy(I have coded several fine items with only a begginers knowlege of php and my_sql when I started) and there isn't any reason why Daniel needs to code all the items you have ideas for.  :D. So cause I had some free time, I was like "Lets write a guide"  O0. And so, without futhur ado...   :)

Item Creation 101
By Basilbeard

So we want to create an item for SMF shop. To do this, first figure what item you want to create. For this lesson, we will be creating a “Magic Die”. Upon using the die, it will target another player, and then either give them a random amount of points or destroy a random amount of points.

Note: This guide assumes a basic knowledge of php. Thought not much is required.

Start by opening the testitem.php file.

Code: [Select]
<?php

/**********************************************\
| SMFSHOP (Shop MOD for Simple Machines Forum) |
|         (c) 2005 DanSoft Australia           |
|      http://www.dansoftaustralia.com/        |
\**********************************************/

//File: testitem.php
//      Test Item (gets some inputs, most likely you will base your items on this)

//VERSION: 1.1 (Build 4)
//DATE: 10th April 2005

//your class should always be called item_filename, eg. if your file is 
//myCoolItem.php then the class should be called 'item_myCoolItem'. This 
//class should always extend itemTemplate.

class item_testitem2 extends itemTemplate {
    
    
//when this function is called, you should set all the item's
    //variables (see inside this example)
    
function getItemDetails() {

        
//VALUES CHANGABLE FROM WITHIN ADMIN PANEL:
          //the name of the item
          
$this->name "A Test Item 2";
          
//the item's description
          
$this->desc "Just a test item! The second time!";
          
//the item's price
          
$this->price 5;

        
//UNCHANGABLE VALUES:
          //whether inputs are required by this item. In this case, we get some inputs,
          //so set this to 'true'.
          
$this->require_input true;
          
//set this to 'false' if the item is unusable. This is good for display
          //items.
          
$this->can_use_item true;
    }

    
//this function is called when the user tries to use the item.
    //if your item needs any further user input then you can get that 
    //input here (eg. if it's a "Change username" item then you have
    //to ask the user what they'd like to change their username to).
    //Any text you return will get shown to the user (DON'T ECHO STUFF).
    
function getUseInput() {
        return 
"A Test: <input type='text' name='test123'>";
    }

    
//the is where all the fun begins. This function is called when 
    //the user actually uses the item. Return stuff, DON'T ECHO!
    
function onUse() {
        return 
"Hello, I am a test!<br>In the text box, you entered '{$_POST['test123']}'";
    }
}

?>

Anything after a // is a comment and doesn’t actually appear in the code, so the real file looks like

Code: [Select]
<?php
class item_testitem2 extends itemTemplate {
    function 
getItemDetails() {
          
$this->name "A Test Item 2";
          
$this->desc "Just a test item! The second time!";
          
$this->price 5;

          
$this->require_input true;
          
$this->can_use_item true;
    }

    function 
getUseInput() {
        return 
"A Test: <input type='text' name='test123'>";
    }

    function 
onUse() {
        return 
"Hello, I am a test!<br>In the text box, you entered '{$_POST['test123']}'";
    }
}

?>
First lets try and see what this item does. The function getUseInput() prompts the user for input upon using the item. This is where they will often type in the member name of the target. The type=’text’ tells them to be a text box while the name=’test123’ defines its value for the onUse() function. In this case, name=’test123’ and so whatever the user inputs here can be called up using ($_POST[‘test123’]} in the onUse() function.

Note that if you wanted two inputs, say, an item that lets you pick a target and also an amount to steal, you would not have.
Code: [Select]
    function getUseInput() {
        return "Target: <input type='text' name='target'>";
        return "Amount to steal: <input type='text' name='steal'>";
    }

Because the return line causes that part of the function to end. Instead, you would need to have.

Code: [Select]
    function getUseInput() {
        return "Target: <input type='text' name='target'> <br>
                Amount to steal: <input type='text' name='steal'>";
    }

Here, the target name could later be called up using {$_POST[‘target’]} and the amount to steal could later be called up using {$_POST[‘steal’]}.

Now lets examine the second half of the code.

Code: [Select]
    function onUse() {
        return "Hello, I am a test!<br>In the text box, you entered '{$_POST['test123']}'";
    }

This is very simple. ‘return’ tells it print the follow text in the quotes. Thus, upon using the item it will display

Hello, I am a test!
In the text box, you entered ‘Arrrrrrrr!’

(this assumes that I did infact enter Arrrrrrr! In the textbox.

So now that we understand the basics behind how this item works, lets start coding out item.

We start by copying this to a new file, which we call MagicDie.php

Code: [Select]
  <?php
class item_testitem2 extends itemTemplate {
    function 
getItemDetails() {
          
$this->name "A Test Item 2";
          
$this->desc "Just a test item! The second time!";
          
$this->price 5;

          
$this->require_input true;
          
$this->can_use_item true;
    }

    function 
getUseInput() {
        return 
"A Test: <input type='text' name='test123'>";
    }

    function 
onUse() {
        return 
"Hello, I am a test!<br>In the text box, you entered '{$_POST['test123']}'";
    }
}

?>


The most important change is in the second line. We need to change class item_testitem2 extends itemTemplate { to class item_MagicDie extends itemTemplate {
We then need to change the name and desc and price. This item requires input and it can be used, so the other two values stay as true. Lets make this item cost 20 points.

Code: [Select]
  <?php
class item_MagicDie extends itemTemplate {
    function 
getItemDetails() {
          
$this->name "Magic Die";
          
$this->desc "Why risk your own points when you can gamble with someone elses?";
          
$this->price 20;

          
$this->require_input true;
          
$this->can_use_item true;
    }

For this item, we also need to include the getAddInput() function. This will allow us, upon the creation of the item, to send bounds on how rewarding or damaging this item can be.
Code: [Select]
    function getAddInput() {
        return "Max amount to give: <input type='text' name='info1' value='100'><br>
               Max amount to take: <input type='text' name='info2' value='-100'>";
    }

Unlike with the getUseInput() function, you cannot change the value of the ‘name’ in these two lines. If you were to add a third, it would have to be called ‘info3’ and a fourth would be ‘info4’. You can’t add anymore after that due to database constraints.

Now that we have that part coded, lets again code our getUseInput() function. This is best served by copying the same function from another item, say the steal item we already have.

Code: [Select]
    function getUseInput() {
global $context, $scripturl, $settings, $txt;
        return "Steal From: <input type='text' name='stealfrom' size='50'>
         <a href='{$scripturl}?action=findmember;input=stealfrom;quote=0;sesc={$context['session_id']}' onclick='return reqWin(this.href, 350, 400);'><img src='{$settings['images_url']}/icons/assist.gif' border='0' alt='{$txt['find_members']}' /> Find Member</a>";
    }

We of course need to make some changes. Namely, the “Steal From” line needs to be changed and the name should be more like ‘dicetarget’ rather than ‘stealfrom’. So we make out changes.

Code: [Select]
    function getUseInput() {
global $context, $scripturl, $settings, $txt;
        return “Choose a target for your die: <input type='text' name='dicetarget' size='50'>
         <a href='{$scripturl}?action=findmember;input=dicetarget;quote=0;sesc={$context['session_id']}' onclick='return reqWin(this.href, 350, 400);'><img src='{$settings['images_url']}/icons/assist.gif' border='0' alt='{$txt['find_members']}' /> Find Member</a>";
    }

Don’t worry about all that funky code. That adds the button that lets you search for member’s names. Now we just need to code the fun part. The onUse() function. The part that actually tells the item what to do.
Code: [Select]
    function onUse() {
        global $db_prefix, $ID_MEMBER, $item_info;

The first task is to make sure they are not targeting themselves with the item. They should use random money if they want to do that. So we do a database query.
Code: [Select]
$result = db_query("SELECT memberName
                                FROM {$db_prefix}members
                                WHERE ID_MEMBER = {$ID_MEMBER}", __FILE__, __LINE__);

I personally hate these queries. The syntax needs to be perfect or else the item will not work, and I often find I need keep fixing the syntax on my items several times after I add them before they actually work. But they are needed for any good item. They are what you use for almost everything, from changing someone’s money count to modifying their title or post count.

We then need to add a few more lines of code. I am not a PHP know-it-all, so can’t really explain why it works I just know it does.

Code: [Select]
$row = mysql_fetch_array($result, MYSQL_ASSOC);
$user = $row[‘memberName’]

This creates an array called row(I think) which contains the previously selected memberName. We can now call that value using $row[‘memberName’]; our next part needs to be an if statement which gets to see if $row[‘memberName’] is the same name as the name the user inputted. If this is true, we need to return an error message instead of executing the random part.

Code: [Select]
if ($user == $_POST[‘dicetarget’]) {
return “Use some random money instead. Use the die on someone else”;
die();
}

According to Daniel, the die() function should stop the item from deleting itself. I have worked around this differently and thus cannot test this, but if it gives you any errors, just remove it from the code.

Now we need to code the other part of the code, what happens when the target is someone different. To do this, we just use an else.
Code: [Select]
else {
$amount = mt_rand($item_info[2], $item_info[1]);

The mt_rand function creates a random integer(inclusive) between the two given bounds. Exactly what we want. We now need to update the other guys point count. Guess what? We need another database query to do this.
Code: [Select]
$result = db_query("UPDATE {$db_prefix}members
                                SET money = money + {$ammount}
                                WHERE memberName = '{$_POST[‘dicetarget’]}'", __FILE__, __LINE__);

So we have updated the database with the random amount. There are two things left to do. The first is to send a friendly PM to the unlucky or lucky target. They should know their points have been changed. To do this, we actually need to add a couple of lines into the top of the code. There again might be a better way to do this, but this is how I’ve figured out how to do it. So until Daniel tells me a better way to do it, add

Code: [Select]
global $sourcedir;
require_once($sourcedir . '/Subs-Post.php');

below the <?php line that starts your code.

Now let’s send the PM. We first need to select the targets ID_MEMBER number so that the PM can be sent.

Code: [Select]
$result = db_query("SELECT ID_MEMBER
       FROM {$db_prefix}members
                           WHERE memberName = '{$_POST[‘dicetarget’]}'", __FILE__, __LINE__);
$row = mysql_fetch_array($result, MYSQL_ASSOC);
$pmfrom = array(
'id' => 1,
'name' => 'Basilbeard',
'username' => 'Basilbeard'
);
$pmto = array(
to' => array($row['ID_MEMBER']),
'bcc' => array()
);
sendpm($pmto, 'Care for a game of chance?', “{$user} uses a Magic Die on you. You check your inventory, and find that your point total has been changed by ".formatMoney($amount).”! [i] You do not need to reply to this automated message”, 0, $pmfrom);                                           

Lastly, we need to return a statement to the user of the item and then make sure we end all our brackets and such.

Code: [Select]
Return “You use your Magic Die on {$_POST[‘dicetarget’}. It spins around for a few minutes, before landing on {$amount} and thus changing their point count by “.formatMoney{$amount}.”!”;
} // This ends the else bracket.
} // This ends the onUse function
} // This items the item function itself.
?> // this ends the PHP code, and thus out file.

So in the end, our whole file looks like:

Code: [Select]
<?php
/**********************************************\
| SMFSHOP (Shop MOD for Simple Machines Forum) |
|         (c) 2005 DanSoft Australia           |
|      http://www.dansoftaustralia.com/        |
\**********************************************/

//File: MagicDie.php
//      Magic Die

//VERSION: 1.0
//DATE: 16th April 2006
//AUTHOR: Basilbeard
global $sourcedir;
require_once(
$sourcedir '/Subs-Post.php');
class 
item_MagicDie extends itemTemplate {
    function 
getItemDetails() {
          
$this->name "Magic Die";
          
$this->desc "Why risk your own points when you can gamble with someone elses?";
          
$this->price 20;

          
$this->require_input true;
          
$this->can_use_item true;
    }

    function 
getAddInput() {
 
   return "Max amount to give: <input type='text' name='info1' value='100'><br>
               Max amount to take: <input type='text' name='info2' value='-100'>"
;
    }

    function 
getUseInput() {
global $context$scripturl$settings$txt;
        return 
"Choose a target for your die: <input type='text' name='dicetarget' size='50'>
         <a href='
{$scripturl}?action=findmember;input=dicetarget;quote=0;sesc={$context['session_id']}' onclick='return reqWin(this.href, 350, 400);'><img src='{$settings['images_url']}/icons/assist.gif' border='0' alt='{$txt['find_members']}' /> Find Member</a>";
    }

    function 
onUse() {
    global $db_prefix$ID_MEMBER$item_info;
$result db_query("SELECT memberName
                                FROM 
{$db_prefix}members
                                WHERE ID_MEMBER = 
{$ID_MEMBER}"__FILE____LINE__);
$row mysql_fetch_array($resultMYSQL_ASSOC);
$user $row['memberName'];
if ($user == $_POST['dicetarget']) {
return "Use some random money instead. Use the die on someone else"
die();
}
else {
$amount mt_rand($item_info[2], $item_info[1]);
$result db_query("UPDATE {$db_prefix}members
                                SET money = money + 
{$ammount}
                                WHERE memberName = '
{$_POST['dicetarget']}'"__FILE____LINE__);
$result db_query("SELECT ID_MEMBER
        FROM 
{$db_prefix}members
                         WHERE memberName = '
{$_POST['dicetarget']}'"__FILE____LINE__);
$row mysql_fetch_array($resultMYSQL_ASSOC);
$pmfrom = array(
'id' => 1,
'name' => 'Basilbeard',
'username' => 'Basilbeard'
);
$pmto = array(
to' => array($row['ID_MEMBER']),
'
bcc' => array()
);
sendpm($pmto, '
Care for a game of chance?', "{$user} uses a Magic Die on you. You check your inventory, and find that your point total has been changed by ".formatMoney($amount)."! [i] You do not need to reply to this automated message[/i]", 0, $pmfrom);                                           
return "You use your Magic Die on {$_POST['
dicetarget']}. It spins around for a few minutes, before landing on {$amount} and thus changing their point count by {$amount} points!";;
}
}
}
?>


Now of course, if I am coding my own items I don’t worry too much about commenting. But if I am doing some coding for others and that, its nice to add some recognition both to myself and to Daniel. So that’s why I put that block of code up at the very top.

The only thing remaining is to upload the item, install it and test it out. For me this never seems to work the first time, or the second time, or even the nth time. I personally prefer to just code the item in something like Crimson Editor, which shows any really dumb mistakes, and then to just upload and let it tell me what I did wrong instead of spending time looking for it myself. For example, the first time I coded this item I forgot the ; on the $user = $row['memberName']; line, causing the whole function not to work.

Once all the errors in the code are sorted out. We need to see if the item works. I start by using it on myself.

Quote
Use some random money instead. Use the die on someone else

Yay! It worked!.

I then try on someone else. And get a mysql error. As I said, I hate those errors. Luckily, this one wasn’t bad. I had {$ammount} instead of {$amount}.

I use it again and get
Quote
You use your Magic Die on Bunny. It spins around for a few minutes, before landing on 52 and thus changing their point count by 52 points!

Yay! The last thing to do is log into Bunny and see the PM. It works! So I assume the item is working just fine and release it on my forum. Or, in this place, remove it from my forum and post this guide.



I'm open to any questions you might have about the guide. Questions about SMF_SHOP itself would probably be better suited for Daniel cause he was the one that acutally wrote it  :P

Update: 4/23: Scroll down for part 2! Learn how to code items which use custom database fields!
« Last Edit: April 24, 2006, 04:42:20 am by Basil Beard »
Arrrrr!

Offline tazpot

Re: Captain Basilbeard's Unoffical Item Creation Guide
« Reply #1 on: April 17, 2006, 06:51:02 am »
Aye captain that be a fine tutorial for the young noobs like myself. This shipmate is gonna be trying to learn to code in php and what a great place to start.

By the way i have been to your site and had a look around, very impressive shop i must say. O0

Offline Daniel15

Re: Captain Basilbeard's Unoffical Item Creation Guide
« Reply #2 on: April 17, 2006, 11:21:51 am »
Nice tutorial :) The only thing I would change would be how you handle the user functions. You use the member name in your queries (eg. UPDATE ......... WHERE memberName = .......). What I'd do is instead get the user's ID, and use that instead. The user's ID is a "database key", so database lookups performed using the ID are faster.

This would mean that the section at the top (the section that checks if the user is using the item on themselves) would be changed. My way would be:

Code: [Select]
global $db_prefix, $ID_MEMBER, $item_info;
$result = db_query("SELECT ID_MEMBER
                                FROM {$db_prefix}members
                                WHERE memberName = {$_POST['dicetarget']}", __FILE__, __LINE__);
$row = mysql_fetch_array($result, MYSQL_ASSOC);
$user = $row['ID_MEMBER']
if ($user = $ID_MEMBER) {
return "Use some random money instead. Use the die on someone else";
die();
}

Quote
I am not a PHP know-it-all, so can’t really explain why it works I just know it does.

Code: [Select]
$row = mysql_fetch_array($result, MYSQL_ASSOC);
$user = $row[‘memberName’]

This creates an array called row(I think) which contains the previously selected memberName. We can now call that value using $row[‘memberName’];
Yes, you're correct. mysql_fetch_array() fetches the current row into an array. Since you did 'SELECT memberName' in the MySQL query, the only array element will be 'memberName'.

Oh, by the way, I stickyed this post :D

Offline Basil Beard

Re: Captain Basilbeard's Unoffical Item Creation Guide
« Reply #3 on: April 17, 2006, 09:11:59 pm »
Oh cool  :D.

Actually, while in a normal situation that would be best; we still need to do the select memberName query in order to tell the target who used magic die them. So we would have to make the same two querys in the end. Your way would be slightly more efficant because the faster query would be used to run the if statment. Thus if you are targeting yourself, only the faster query would be done.
Arrrrr!

Offline tazpot

Re: Captain Basilbeard's Unoffical Item Creation Guide
« Reply #4 on: April 18, 2006, 08:23:04 am »
What do you think of this ? it is my first attempt at knocking a new item together and thought that rather than test it on the site i would get you guy's to give it the once over first and point out any obvious errors.

Code: [Select]
<?php
/**********************************************\
| SMFSHOP (Shop MOD for Simple Machines Forum) |
|         (c) 2005 DanSoft Australia           |
|      http://www.dansoftaustralia.com/        |
\**********************************************/

//File: KarmaBash.php
//      Item

//VERSION: 1.1 (Build 4)
//DATE: 10th April 2005

class item_KarmaBash extends itemTemplate {
    function 
getItemDetails() {
        
$this->name "Karma Bash";
        
$this->desc "Bash 10 points of another members karma";
        
$this->price 50;

        
$this->require_input true;
        
$this->can_use_item true;
    }

    function 
getUseInput() {
global $context$scripturl$settings$txt;
        return 
"Karma Bash: <input type='text' name='bashwho' size='50'>
         <a href='
{$scripturl}?action=findmember;input=bashwho;quote=0;sesc={$context['session_id']}' onclick='return reqWin(this.href, 350, 400);'><img src='{$settings['images_url']}/icons/assist.gif' border='0' alt='{$txt['find_members']}' /> Find Member</a>";
    }
         function 
onUse() {
        global 
$db_prefix$ID_MEMBER$item_info;

$result db_query("UPDATE {$db_prefix}members
                               SET karmaBad = karmaBad + 10
                               WHERE membername = 
{$_POST['bashwho']}",
                               
__FILE____LINE__);
   return "Successfully bashed 10 karma points from {$_POST['bashwho']}!";
$result db_query("SELECT ID_MEMBER
       FROM 
{$db_prefix}members
                           WHERE memberName = '
{$_POST[‘bashwho’]}'"__FILE____LINE__);
$row mysql_fetch_array($resultMYSQL_ASSOC);
$pmfrom = array(
'id' => 1,
'name' => 'Tazpot',
'username' => 'Tazpot'
);
$pmto = array(
to' => array($row['ID_MEMBER']),
'
bcc' => array()
);
sendpm($pmto, '
You have been bashed', “{$user} bashed 10 points off your karma."”! [i] You do not need to reply to this automated message”, 0, $pmfrom);
}
}

?>

I have used bits of code from increasekarma.php and fiddled with it a bit. Any tips or advice would be great.
« Last Edit: April 18, 2006, 08:59:17 am by tazpot »

Offline Basil Beard

Re: Captain Basilbeard's Unoffical Item Creation Guide
« Reply #5 on: April 18, 2006, 10:32:30 am »
First off, INDENT INDENT INDENT! Seriously, code is much easier to follow when it is properly indented. (general rule of thumb is one tab for each bracket)

Secondly, you need to return a message to the user. I am not sure what happens if you dont. It might work fine, but its nice to give the user a message as well.

Lastly, Im not sure what those quotes are doing after "karma" but they shouldn't be there and will mess up your code. =).

Other than that, seems like you've done a pretty good job =D

So the fixed code would look like

Code: [Select]
<?php
/**********************************************\
| SMFSHOP (Shop MOD for Simple Machines Forum) |
|         (c) 2005 DanSoft Australia           |
|      http://www.dansoftaustralia.com/        |
\**********************************************/

//File: KarmaBash.php
//      Item

//VERSION: 1.1 (Build 4)
//DATE: 10th April 2005

class item_KarmaBash extends itemTemplate {
    function 
getItemDetails() {
        
$this->name "Karma Bash";
        
$this->desc "Bash 10 points of another members karma";
        
$this->price 50;

        
$this->require_input true;
        
$this->can_use_item true;
    }

    function 
getUseInput() {
global $context$scripturl$settings$txt;
        return 
"Karma Bash: <input type='text' name='bashwho' size='50'>
         <a href='
{$scripturl}?action=findmember;input=bashwho;quote=0;sesc={$context['session_id']}' onclick='return reqWin(this.href, 350, 400);'><img src='{$settings['images_url']}/icons/assist.gif' border='0' alt='{$txt['find_members']}' /> Find Member</a>";
    }

function onUse() {
        global 
$db_prefix$ID_MEMBER$item_info;

$result db_query("UPDATE {$db_prefix}members
                           SET karmaBad = karmaBad + 10
                           WHERE membername = 
{$_POST['bashwho']}",
                           
__FILE____LINE__);
    return "Successfully bashed 10 karma points from {$_POST['bashwho']}!";
$result db_query("SELECT ID_MEMBER
         FROM 
{$db_prefix}members
               WHERE memberName = '
{$_POST[‘bashwho’]}'"__FILE____LINE__);
$row mysql_fetch_array($resultMYSQL_ASSOC);
$pmfrom = array(
'id' => 1,
'name' => 'Tazpot',
'username' => 'Tazpot'
);
$pmto = array(
'to' => array($row['ID_MEMBER']),
'bcc' => array()
);
sendpm($pmto'You have been bashed'"{$user} bashed 10 points off your karma! [i] You do not need to reply to this automated message[/i]"0$pmfrom);
return "You have bashed {$_POST['bashwho']} for 10 points off their karma!";
}
}
?>

Arrrrr!

Offline tazpot

Re: Captain Basilbeard's Unoffical Item Creation Guide
« Reply #6 on: April 19, 2006, 06:26:37 am »
 ;D cheers Captain for a great tutorial i think using it i have already learnt a lot as this was my first ever attempt at php at all. As soon as i get chance i will add this to my board and let you know how i got on. O0

Offline Basil Beard

Re: Captain Basilbeard's Unoffical Item Creation Guide
« Reply #7 on: April 24, 2006, 03:23:19 am »
Part 2:

Last time we looked at how to code a simple item. But often people get board of these basic items, and what to expand their horizons. The best way to code fun interesting items is to add more database fields. To do this, you need to either use a program such as phpMyAdmin, or simply run an install query. When you do this, you need to be very sure not to mess up any of the existing database or serious problems could occur. Most likely you will only be modifying the smf_members and smf_shop_inventory tables. Smf_members controls all the stats for a given member. If you wanted to say, make an item which added something below each members post like the Flag Item thingy, youd modify this table. Smf_shop_iventory controls the actual items, and this is what we will be looking at today.

Say we want to make an item that isn’t single use but also isn’t multiple use. We will call this item a “Wizard’s Wand”. The Wizard’s Wand will be a type of gambling item. Each time you use it, it gives you 10 points. The more its used, however, the more likely it will break.

The first thing to do is add a “counter” row to the smf_shop_inventory table. This will count how many times the Wizard’s Wand has been used. We can them use the mt_rand function to see if the wand breaks or not. To add this, either use some sort of program or upload and run the following query

Code: [Select]
db_query("ALTER TABLE `{$db_prefix}shop_inventory` ADD `counter` INT(10) UNSIGNED DEFAULT '0' NOT NULL", __FILE__, __LINE__);
Then make sure you remove this file from your server as it posses a security risk.

If you use a program to add it, just make sure the row is named “counter” and is an unsigned int(10) with a default value of zero.

This item contains no input from the user, so the first part of our code looks like this.

Code: [Select]
class item_WizardsWand extends itemTemplate {
   
    //when this function is called, you should set all the item's
    //variables (see inside this example)
    function getItemDetails() {

        //VALUES CHANGABLE FROM WITHIN ADMIN PANEL:
          //the name of the item
          $this->name = "Wizard's Wand";
          //the item's description
          $this->desc = "Wave the wand and get some points. Make sure it doesn't explode though!";
          //the item's price
          $this->price = 30;
         
        //UNCHANGABLE VALUES:
          //whether the item requires input or not. In this case, we don't need
          //any input
          $this->require_input = false;
          //set this to 'false' if the item is unusable. This is good for display
          //items.
          $this->can_use_item = true;
    }
   
        function getAddInput() {
        return "Amount to give per use: <input type='text' name='info1' value='10'>
        "Max number of uses: <input type='text' name='info2' value='6'>";
    }

Now for the fun part. We first start by selecting the value of counter from the proper table. We want the column where the id value is the same as the item we are using(individual items have their own unique id values in the smf_shop_inventory table). To do this, we use the following section of code:
Code: [Select]
function onUse() {
global $db_prefix, $ID_MEMBER, $memberName, $item_info;
$result = db_query(" SELECT counter
FROM {$db_prefix}shop_inventory
WHERE id = {$_GET['id']}", __FILE__, __LINE__);
$row = mysql_fetch_array($result, MYSQL_ASSOC);
This is similar to the code we used in the first section of this tutorial.

Next we need to see if this item breaks or not. $item_info[2] holds the max number of uses. After zero uses, this item is sure to work, and after $item_info[2] uses it is sure to fail. After 2 uses, it should break 2/$item_info[2] of the time. So to see if it works, we first select the numerator of our fraction. This should be a number between 1 and $item_info[2]. So we use

Code: [Select]
$value = mt_rand(1, $item_info[2]);

Now we want to compare $value/$item_info[2] to $row[‘counter’]/$item_info[2]. This, of course, is the same as comparing $value to $row[‘counter’]. If they are equal, or $row[‘counter’] is greater than value, then the item should break. Else wise is should work. So we use a if statement.

Code: [Select]
(if $value <= $row[‘counter’]) {
$result = db_query("UPDATE {$db_prefix}members
  SET money = money - $item_info[1]
WHERE ID_MEMBER = {$ID_MEMBER}", __FILE__, __LINE__);
return “You try to use the Wizard’s wand, but it backfires and destroys {$item_info[1]} of your points!”;
}

If the if statement is false, the item should work, give the user $item_info[1] points, increase the counter by one, not delete itself, and return a successful message.

Code: [Select]
Else {
$result = db_query("UPDATE {$db_prefix}members
  SET money = money + $item_info[1]
WHERE ID_MEMBER = {$ID_MEMBER}", __FILE__, __LINE__);
$result = db_query("UPDATE {$db_prefix}shop_inventory
  SET counter = counter + 1
WHERE id = {$_GET[‘id’]}", __FILE__, __LINE__);
die();
return “You wave your Wizard’s wand an cause {$item_info[1]} points to appear!”;
}
}
}
?>

So this time the final code looks like:    

Code: [Select]
<?php
class item_WizardsWand extends itemTemplate {
    
    
//when this function is called, you should set all the item's
    //variables (see inside this example)
    
function getItemDetails() {

        
//VALUES CHANGABLE FROM WITHIN ADMIN PANEL:
          //the name of the item
          
$this->name "Wizard's Wand";
          
//the item's description
          
$this->desc "Wave the wand and get some points. Make sure it doesn't explode though!";
          
//the item's price
          
$this->price 30;
          
        
//UNCHANGABLE VALUES:
          //whether the item requires input or not. In this case, we don't need
          //any input
          
$this->require_input false;
          
//set this to 'false' if the item is unusable. This is good for display
          //items.
          
$this->can_use_item true;
    }
    
        function 
getAddInput() {
 
       return "Amount to give per use: <input type='text' name='info1' value='10'>
         "
Max number of uses: <input type='text' name='info2' value='10'>";
    }
    
function onUse() {
global 
$db_prefix$ID_MEMBER$memberName$item_info;
$result = db_query(" SELECT counter
FROM {$db_prefix}shop_inventory
WHERE id = {$_GET['id']}", __FILE__, __LINE__);
$row = mysql_fetch_array($result, MYSQL_ASSOC);
$value = mt_rand(1, $item_info[2]);
if (
$value <= $row['counter']) {
$result = db_query("UPDATE {$db_prefix}members
 
SET money money $item_info[1]
     WHERE ID_MEMBER = {$ID_MEMBER}", __FILE__, __LINE__);
return "
You try to use the Wizard's wand, but it backfires and destroys {$item_info[1]} of your points!";
}
else {
$result = db_query("UPDATE {$db_prefix}members
SET money = money + $item_info[1]
WHERE ID_MEMBER = {$ID_MEMBER}", __FILE__, __LINE__);
$result = db_query("UPDATE {$db_prefix}shop_inventory
    SET counter = counter + 1
WHERE id = {$_GET['
id']}", __FILE__, __LINE__);
die();
return "You wave your Wizard'
s Wand and cause {$item_info[1]} points to appear!";
}
}
}
?>



A few words of note:

1)   This code hasn’t been tested yet and thus might contain some bugs. Sorry about that.
2)   Be sure to reuse database rows. If you can make “counter” serve for 4-5 items, then that’s great! You’ve saved space. No one cares if the row name actually makes sense!
3)   Play around with the price as well to make sure this doesn’t give your members too many points. :-P.
« Last Edit: April 24, 2006, 03:45:03 am by Basil Beard »
Arrrrr!

Offline Daniel15

Re: Captain Basilbeard's Unoffical Item Creation Guide(Part 2 is out)
« Reply #8 on: April 25, 2006, 04:01:24 pm »
Nice work Basil Beard! I might feature this on the SMFShop page on DanSoft Australia soon (I already have a link to this topic on my homepage).

Just one minor problem I saw (sorry, I didn't have time to thoroughly read the whole thing :P).
Code: [Select]
       function getAddInput() {
        return "Amount to give per use: <input type='text' name='info1' value='10'>
        "Max number of uses: <input type='text' name='info2' value='10'>";
    }
   
This should be:
Code: [Select]
       function getAddInput() {
        return "Amount to give per use: <input type='text' name='info1' value='10'>
        Max number of uses: <input type='text' name='info2' value='10'>";
    }
You accidentally placed an extra " in it (you can tell because the syntax highlighing went weird above :P). Apart from that, it looks good! I'll have a more thorough read of it on Friday, and test out your item!

Again, thanks! I really appreciate this :D

Offline Basil Beard

Re: Captain Basilbeard's Unoffical Item Creation Guide(Part 2 is out)
« Reply #9 on: April 25, 2006, 10:00:02 pm »
I always make little coding errors like that. Not really sure why. I posted this with out having time to test it, because I was pressed for time.
Arrrrr!

Offline tazpot

Re: Captain Basilbeard's Unoffical Item Creation Guide(Part 2 is out)
« Reply #10 on: May 03, 2006, 09:40:39 am »
I have tried this new item and i think it will be good but i have a problem, when you try to use the item i just get a white page with no error message :-\  but this is where the address bar points

   smf/index.php?action=shop;do=inv3;id=88

Any ideas?

Offline Basil Beard

Re: Captain Basilbeard's Unoffical Item Creation Guide(Part 2 is out)
« Reply #11 on: May 03, 2006, 10:18:41 am »
Sorry, not sure whats up. Apparently it isn't liking going straight to step 3. I'll look into this when I have more time.
Arrrrr!

Offline tazpot

Re: Captain Basilbeard's Unoffical Item Creation Guide(Part 2 is out)
« Reply #12 on: May 03, 2006, 10:32:56 am »
Cheers mate  O0   i will have another go tomorrow cos my head hurts now and it's late.

Offline tazpot

Re: Captain Basilbeard's Unoffical Item Creation Guide(Part 2 is out)
« Reply #13 on: May 09, 2006, 07:33:38 am »
 :'( I still can't get this to work  :'(  i have tried re-installing it but to no avail.

Offline chrisjs162216

Re: Captain Basilbeard's Unoffical Item Creation Guide(Part 2 is out)
« Reply #14 on: May 11, 2006, 07:30:20 am »
Part one may not have worked because of a typo.  There should be a single quote ( ' ) before to.

Try this:

Code: [Select]
<?php
/**********************************************\
| SMFSHOP (Shop MOD for Simple Machines Forum) |
|         (c) 2005 DanSoft Australia           |
|      http://www.dansoftaustralia.com/        |
\**********************************************/

//File: MagicDie.php
//      Magic Die

//VERSION: 1.0
//DATE: 16th April 2006
//AUTHOR: Basilbeard
global $sourcedir;
require_once(
$sourcedir '/Subs-Post.php');
class 
item_MagicDie extends itemTemplate {
    function 
getItemDetails() {
          
$this->name "Magic Die";
          
$this->desc "Why risk your own points when you can gamble with someone elses?";
          
$this->price 20;

          
$this->require_input true;
          
$this->can_use_item true;
    }

    function 
getAddInput() {
 
   return "Max amount to give: <input type='text' name='info1' value='100'><br>
               Max amount to take: <input type='text' name='info2' value='-100'>"
;
    }

    function 
getUseInput() {
global $context$scripturl$settings$txt;
        return 
"Choose a target for your die: <input type='text' name='dicetarget' size='50'>
         <a href='
{$scripturl}?action=findmember;input=dicetarget;quote=0;sesc={$context['session_id']}' onclick='return reqWin(this.href, 350, 400);'><img src='{$settings['images_url']}/icons/assist.gif' border='0' alt='{$txt['find_members']}' /> Find Member</a>";
    }

    function 
onUse() {
    global $db_prefix$ID_MEMBER$item_info;
$result db_query("SELECT memberName
                                FROM 
{$db_prefix}members
                                WHERE ID_MEMBER = 
{$ID_MEMBER}"__FILE____LINE__);
$row mysql_fetch_array($resultMYSQL_ASSOC);
$user $row['memberName'];
if ($user == $_POST['dicetarget']) {
return "Use some random money instead. Use the die on someone else"
die();
}
else {
$amount mt_rand($item_info[2], $item_info[1]);
$result db_query("UPDATE {$db_prefix}members
                                SET money = money + 
{$ammount}
                                WHERE memberName = '
{$_POST['dicetarget']}'"__FILE____LINE__);
$result db_query("SELECT ID_MEMBER
        FROM 
{$db_prefix}members
                         WHERE memberName = '
{$_POST['dicetarget']}'"__FILE____LINE__);
$row mysql_fetch_array($resultMYSQL_ASSOC);
$pmfrom = array(
'id' => 1,
'name' => 'Basilbeard',
'username' => 'Basilbeard'
);
$pmto = array(
'to' => array($row['ID_MEMBER']),
'bcc' => array()
);
sendpm($pmto'Care for a game of chance?'"{$user} uses a Magic Die on you. You check your inventory, and find that your point total has been changed by ".formatMoney($amount)."! [i] You do not need to reply to this automated message[/i]"0$pmfrom);                                           
return "You use your Magic Die on {$_POST['dicetarget']}. It spins around for a few minutes, before landing on {$amount} and thus changing their point count by {$amount} points!";;
}
}
}
?>



Chris