After reading tazpot's post
Here I decided to take him up on it.
. 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.
. So cause I had some free time, I was like "Lets write a guide"
. And so, without futhur ado...
Item Creation 101By BasilbeardSo 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.
<?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
<?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.
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.
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.
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
<?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.
<?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.
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.
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.
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.
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.
$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.
$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.
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.
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.
$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
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.
$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.
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:
<?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($result, MYSQL_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($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[/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.
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
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
Update: 4/23: Scroll down for part 2! Learn how to code items which use custom database fields!