Enquirer Home Page | Twitter | Back to Improbable Island

 Forum Index > Season Two > Development New Topic Post Reply
 Venting of Frustrations
 |  Printable Version
CavemanJoe
 Monday, August 30 2010 @ 06:12 AM UTC (Read 2882 times)  
Forum Admin
Admin

Status: offline

Registered: 02/24/08
Posts: 2281

So I wrote this IItems system. The biggest goal was to have something where any stat could be assigned to any iitem, and it had to be fast..

A sane person would have used four database tables, laid out like this:

IITEMS_MASTER, where definitions for names of items are stored.
Columns: ID (auto-incrementing integer), IITEMID (varchar,255 - for example, giftbox1), ACTIVATED (boolean - this is for telling the game about whether you're in the middle of building an item and don't want to activate it for public consumption just yet).

IITEM_SETTINGS, where all the properties of brand-new, unused items are stored.
Columns: ITEM (references the ItemID in the above table, for example, giftbox1), SETTING (for example, "giftable"), value (for example, "true").

IITEMS_PLAYER, which is a list of all the items in the game and who owns them.
Columns: ID (auto-incrementing integer), ITEMID (for example, giftbox1), OWNER (for example, 1 (being my acctid)).

IITEM_PREFS, which is a list of all the attributes for all the items in the game that might change over time (for example, item hitpoints, how rusty a blade is getting, and so forth)
Columns: ITEM (referencing the ID in the IITEMS_PLAYER table), SETTING (for example, "used"), VALUE (for example, "false").

Now, that's just off the top of my head, and isn't ideal. For example, the first table seems a bit redundant when code can make an educated guess on what items are in the game by looking at the second table (not much point having an item if it doesn't have any properties, eh? (properties include image path, full name, description and so on as well as things like magic power and so forth)).

But, said I, one year ago, young and naive, that means four database lookups! Or one unwieldy statement looking at four tables! My server's running at a crawl as it is already! Hey, I know - let's get clever about this! Let's have one iitems table for the master definitions, then we'll go into the moduleprefs table and store each player's Inventory ALL IN ONE TABLE ROW. No point making another new table when we've got this one lying around handy, eh?

*forehead slap*

So, serialized arrays.

One lookup, one Inventory, complete with all the relevant ID's, from which you can look in the master iitems table for details about the iitems contained therein. Pretty elegant, no?

But it could be yet more elegant! What if we save ourselves the lookup to the master iitems table - what if we copied over certain aspects of the iitems in question, I mean, like, frequently-queried aspects like their name and image path, into the player's Inventory? Then we wouldn't need to look at the master table! I ARE GENIUS! I'll just have to remember which properties are likely to be queried!

*forehead slap*

If one were to put something inside a gift box and then give the gift box to another player... in the sane system, you'd do something like this:

Let's assume that your gift box is itemid 4001, and in it, you are to put a cunning disguise, itemid 4000 (which you've just purchased a second ago).

Add a row to the iitems_prefs table (naturally you'd do this in a function, but for the sake of example...) telling the database that itemid 4001 (the box) contains itemid 4000 (the disguise). Actually yeah, let's pull a function out of my bum right now to do that:

PHP Formatted Code
set_iitem_pref("contains",4000,4001);

Set the owner of itemid 4000 (the disguise) to zero, just to get it out of the way until somebody owns it again. We'll do this in a function too, but just to show you how easy it is:
PHP Formatted Code
UPDATE iitems_player SET owner = 0 WHERE id = 4000


Change the owner of the gift box by going into the iitems_player table and changing the "owner" field. YES, YOU CHANGE THE OWNER BY CHANGING THE OWNER. GENIUS HUH WELL WHY DIDN'T I THINK OF DOING THAT THEN.

PHP Formatted Code
UPDATE iitems_player SET owner = (giftee's account id) WHERE id = 4001


When the player opens the gift box, change the owner of the disguise so that it's the same as the owner of the box, then delete the box (DELETE THE BOX FROM THE GAME BY DELETING ITS ENTRY FROM THE TABLE HOLY SHIT YOU'D THINK THIS WOULD HAVE BEEN OBVIOUS).
Job done.

Four lines of code.

I'm not even gonna go in to how much work it is to transfer an iitem from one player to another and have all its preferences intact, in the system we have at the moment. It's horrible. It's awful. It's so, so complicated. There are incantations. I'm just gonna say that when I was testing this new Hunter's Lodge, I did a very stupid thing. I bought an item, put it into a box, and took it out again and said "Right, that works."

My IItems system is built for speed and flexibility - not for being robust, easy-to-use, or without its Little Eccentricities. It's preposterously persnickety. Predictably, when you put a pair of Groucho glasses and a false moustache into a box, then give it to someone else, the box miraculously becomes empty. This on top of the Dwellings cataclysm makes me think I should stop coding for things that whizz along at a hundred miles an hour, because when you're whizzing along at a hundred miles an hour, it's much easier to crash - and start coding for things that amble along at their own good pace, drawing a few dozen extra database lookups if they need to but damn it not spawning devil boxes that eat people's presents and then disappear.

Mutter mumble...

Mutter...

"Oh, serialized arrays, that'll put less stress on the database server and make the site go faster!" Yeah, 2009 CMJ, you know what else it'll make go faster? YOUR SODDING HAIR, THAT'S WHAT.


 
Profile Email Website
Quote
Harris
 Monday, August 30 2010 @ 06:17 AM UTC  
Forum Improbable Badass
Improbable Badass

Status: offline

Registered: 11/24/09
Posts: 456

*offers an Oreo*


"Ain't nothin' left to do but smile, smile, smile." -The Grateful Dead
 
Profile Email Website
Quote
Johnson
 Monday, August 30 2010 @ 06:31 AM UTC  
Forum Contender
Contender

Status: offline

Registered: 06/01/10
Posts: 45

*sits CMJ down, puts out a pot of strong tea and some pancakes. and then a couple of beers. because those all go together, yeah.*

you are wonderful, and we love you. this place would not exist without you. don't beat yourself up too much because even with bugs, II is well worth however many hours we spend plugged into it. Maestro, we who are about to play salute you. For being amazing.


 
Profile Email
Quote
CavemanJoe
 Monday, August 30 2010 @ 06:51 AM UTC  
Forum Admin
Admin

Status: offline

Registered: 02/24/08
Posts: 2281

You guys are great. But this can't stay the way it is. The stuff Emily's working on is IItems- and Dwellings-heavy, and the systems we're using likely won't hold up to the things we want them to do; at least, not without some serious modifications.

Dwellings is great - it's one of the few times I've used a serialized array when it actually makes sense to do so. But they're so fragile, as we've seen. The only reason to split those up into data stored in different database rows would be for redundancy's sake - making it so that if something goes Horribly Wrong, only a room or a description is lost, not the whole thing.

Do I rewrite IItems and Dwellings for future flexibility and let you lot go without new content for as long as that takes, or struggle on with the current systems and hope it all doesn't come crashing down? It's a dilemma worthy of many cups of tea and cigarettes, I tell ye.


 
Profile Email Website
Quote
Harris
 Monday, August 30 2010 @ 06:56 AM UTC  
Forum Improbable Badass
Improbable Badass

Status: offline

Registered: 11/24/09
Posts: 456

In all levity AND seriousness- take your time. We'll amuse ourselves. I guarantee it.


"Ain't nothin' left to do but smile, smile, smile." -The Grateful Dead
 
Profile Email Website
Quote
CavemanJoe
 Monday, August 30 2010 @ 07:32 AM UTC  
Forum Admin
Admin

Status: offline

Registered: 02/24/08
Posts: 2281

Quote by: Harris

In all levity AND seriousness- take your time. We'll amuse ourselves. I guarantee it.



You guys are good at that. Maybe some roleplaying tools in the meantime would help keep you busy...


 
Profile Email Website
Quote
Johnson
 Monday, August 30 2010 @ 07:50 AM UTC  
Forum Contender
Contender

Status: offline

Registered: 06/01/10
Posts: 45

Quote by: Harris

In all levity AND seriousness- take your time. We'll amuse ourselves. I guarantee it.




agreed. building up strong structure is more important. good foundations, otherwise the whole house of cards comes tumbling down with a flick of the wrist later on when there's so much more sitting on top of it (hurrrah, a bigger boom!). you've developed the whole thing way more than your average LoGD or offshoots- we've enough to amuse ourselves with for now, and for a while. and amusing as it was for gameplay possibilities this round, another Cataclysm is probably one too many Razz.

in summation: WE LOVE YOU. take your time. we are small-minded creatures. give us a rattle and we'll give you wars, tragedies, romances and comedy back.


 
Profile Email
Quote
Kash
 Monday, August 30 2010 @ 08:10 AM UTC  
Forum Improbable Badass
Improbable Badass

Status: offline

Registered: 12/11/09
Posts: 149

Quote by: Johnson

... another Cataclysm is probably one too many...



I... actually like cataclysms? They interest me. They are... unusual.


 
Profile Email
Quote
CavemanJoe
 Monday, August 30 2010 @ 08:14 AM UTC  
Forum Admin
Admin

Status: offline

Registered: 02/24/08
Posts: 2281

LOOK AT HOW HORRIBLY COMPLICATED THIS BLOODY THING IS

PHP Formatted Code
<?php

/*
=======================================================
GET PLAYER INVENTORY
Returns the player's inventory in an array.  This is basically an unassociated multidimensional array with item data in each slot.
=======================================================
*/


function iitems_get_player_inventory($userid=false) {
        global $session;
        if ($userid === false) $userid = $session['user']['acctid'];
        $items = unserialize(get_module_pref("items", "iitems", $userid));
        if (!is_array($items)) {
                $items = array();
                set_module_pref("items", serialize($items), "iitems", $userid);
        }
        //Check if array key zero is set - this makes iitems_has_item work a bit nicer
        if (isset($items[0])){
                $items[]=$items[0];
                unset($items[0]);
                set_module_pref("items", serialize($items), "iitems", $userid);
        }
        return $items;
}

/*
=======================================================
SET PLAYER INVENTORY
Reserializes and writes back the player's inventory.
=======================================================
*/


function iitems_set_player_inventory($inventory=false,$userid=false) {
        global $session;
        if ($userid === false) $userid = $session['user']['acctid'];
        if (!is_array($inventory)){
                return false;
        }
        //debug($inventory);
        set_module_pref("items", serialize($inventory), "iitems", $userid);
}

/*
=======================================================
GET ITEM DETAILS
Returns master details array of the item whose localname is given.
=======================================================
*/


function iitems_get_item_details($localname){
        $sql = "SELECT id,localname,data FROM " . db_prefix("iitems") . " WHERE localname = '$localname'";
        $result = db_query_cached($sql,"iitems-".$localname);
        $row=db_fetch_assoc($result);
        $sarray = unserialize($row['data']);
        return $sarray;
}

/*
=======================================================
GET ALL ITEM DETAILS
Returns master details array of all items in the database - for giving random items, for example.
=======================================================
*/


function iitems_get_all_item_details(){
        $sarray = array();
        $sql = "SELECT id,localname,data FROM " . db_prefix("iitems") . "";
        $result = db_query($sql);
        for ($i=0;$i<db_num_rows($result);$i++){
                $row=db_fetch_assoc($result);
                $sarray[$row['localname']] = unserialize($row['data']);
        }
        return $sarray;
}


/*
=======================================================
BASIC PLAYER FUNCTIONALITY
Now beginning basic player functionality section.
=======================================================
*/


/*
=======================================================
GIVE ITEM
Gives an item to a player by copying the item array into the player's inventory.  Includes a hook to modify the item in question.
If the item type is "Normal," then when an item already exists in the player's Inventory the Quantity is increased by one.
If the item type is "Special," then a new copy of the item is created and its "Instance" flag is set, helping the player to distinguish between copies of the same item.
Modules hooking into "iitems-give-item" should add to their $args array any parameters which should be copied from the database to the player's inventory array - for example, stats that can be altered on a per-player, per-item basis, such as item hitpoints, usepoints, or condition.  Hooks should also be added, to save looking up the item at each hook.
If modules hooking into "iitems-give-item" add the 'blockadd' parameter, the item will not be added.
Returns true if adding item was successful, false if unsuccessful.
=======================================================
*/


function iitems_give_item($itemid,$userid=false,$itemarray=false){
        global $session;
        //debug("giving iitem ".$itemid);
        if ($userid == false) $userid = $session['user']['acctid'];
       
        $inventory = iitems_get_player_inventory($userid);
       
        if (is_array($itemarray)){
                //here, we've been given an item array.  It's probably a special item IN FACT IT'S PROBABLY A BLOODY GIFT BOX WITH SOMETHING INSIDE, so let's treat it like one.  This will do until we rewrite this bag of utter toss.
                $inventory[]=$itemarray;
        } else {
                $iteminfo = iitems_get_item_details($itemid);
                if (!$iteminfo){
                        debug("ERROR: undefined iitem (trying to give ".$itemid." to acctid ".$userid);
                        return false;
                }
               
                $item = array();
                $item['itemid'] = $itemid;
                $item['verbosename'] = $iteminfo['verbosename'];
                //default location to store item is in main inventory - change via modulehook "iitems-give-item" if necessary
                if ($iteminfo['inventorylocation']){
                        $item['inventorylocation']=$iteminfo['inventorylocation'];
                } else {
                        $item['inventorylocation']="main";
                }
                if ($iteminfo['villagehooknav']) $item['villagehooknav'] = true;
                if ($iteminfo['foresthooknav']) $item['foresthooknav'] = true;
                if ($iteminfo['worldnavhooknav']) $item['worldnavhooknav'] = true;
                if ($iteminfo['fightnav']) $item['fightnav'] = true;
                if ($iteminfo['blocktransfer']) $item['blocktransfer'] = true;
               
                //To save database lookups, every time we send an item through a hook, we send it as part of an array.
                $hookitem = array();
                $hookitem['master'] = $iteminfo;
                $hookitem['player'] = $item;
               
                $hookitem = modulehook("iitems-give-item",$hookitem);
                $item = $hookitem['player'];
               
                if ($item['blockadd']){
                        debug("Blockadd param inserted");
                        debug("Failed to give iitem because of blockadd parameter (trying to give ".$itemid." to acctid ".$userid);
                        return false;
                }
               
                //Check to see if we're adding an inventory-type item, IE backpack etc, and if so, add it and return early:
                if ($iteminfo['type']=="inventory"){
                        $inventory[$iteminfo['inventorylocation']] = $item;
                        debug("Item is inventory-type item");
                        set_module_pref("items", serialize($inventory), "iitems", $userid);
                        return true;
                }
               
                //Now find out whether the player already has this item
                $invid = iitems_has_item($itemid, $inventory, $userid, $item['inventorylocation']);
                if ($invid===false){
                        //player does not have the item, copy it to the player's Inventory
                        if ($iteminfo['type']=="normal"){
                                //set initial quantity
                                $item['quantity'] = 1;
                        }
                        $inventory[] = $item;
                } else {
                        //Player already has the item - check for special items
                        if ($iteminfo['type']=="special"){
                                $instance = 1;
                                $inventory2 = $inventory;
                                foreach($inventory2 AS $inventoryitem => $details){
                                        if ($details['verbosename'] == $item['verbosename']){
                                                $instance++;
                                        }
                                }
                                $item['instance'] = $instance;
                                $inventory[] = $item;
                        } else if ($iteminfo['type']=="normal"){
                                $inventory[$invid]['quantity'] += 1;
                        } else {
                                debug("ERROR: Item type not set (trying to give ".$itemid." to acctid ".$userid);
                                return false;
                        }
                }
        }
       
        //debug($inventory);
        set_module_pref("items", serialize($inventory), "iitems", $userid);
        debug("SUCCESS: Item ".$itemid." given successfully to account ".$userid);
        return true;
}

/*
=======================================================
PLAYER HAS ITEM ID?
Does the player already have an item which matches this itemid or verbose name?  Returns the item's numerical array key if true, false if false.
If player's inventory is passed, saves a database lookup.

TODO:
This really needs to return an array containing the master iitem, the player iitem, and the inventory key. :-/

TODO:
Sometimes an iitem will be in slot 0 - this means you have to do something like:
if (!iitems_has_item || iitems_has_item===0)
rather than just !iitems_has_item.

TODO:
Having to pass the invloc each time is a pain in the ass.

=======================================================
*/


function iitems_has_item($search,$inventory=false,$userid=false,$invloc="main"){
        global $session;
        if ($userid === false) $userid = $session['user']['acctid'];
        //get item info and player inventory
        if (!is_array($inventory)) $inventory = iitems_get_player_inventory($userid);
       
        if (is_numeric($search)){
                if (isset($inventory[$search]) && $inventory[$search]['inventorylocation']==$invloc){
                        return $search;
                }
        }
        foreach($inventory AS $item => $details){
                if ($details['itemid']==$search  && $details['inventorylocation']==$invloc){
                        return $item;
                }
        }
        foreach($inventory AS $item => $details){
                if ($details['verbosename']==$search  && $details['inventorylocation']==$invloc){
                        return $item;
                }
        }
        return false;
}

/*
=======================================================
GET IITEM
Returns an array containing the player's matching iitems and the master iitem.  Handy for basic functionality.

Example - curse all player's swords:
$inv = iitems_get_player_inventory();
$item = iitems_get_item("sword",false,$inv);
foreach($item['player'] AS $item=>$vals){
        $inv[$item]['cursed']=1;
        $inv[$item]['attackpower']-=1;
}
iitems_set_player_inventory($inv);

Example - make all player's throwing knives rusty if they are kept in his coal shed:
$inv = iitems_get_player_inventory();
$item = iitems_get_item("throwingknife",false,$inv);
foreach($item['player'] AS $item=>$vals){
        if ($vals['inventorylocation']=="coalshed"){
                $inv[$item]['rust']+=1;
                $inv[$item]['attackpower']-=1;
        }
}
iitems_set_player_inventory($inv);

=======================================================
*/


function iitems_get_item($search,$userid=false,$inventory=false){
        global $session;
        if ($userid === false) $userid = $session['user']['acctid'];
        //get item info and player inventory
        if (!is_array($inventory)) $inventory = iitems_get_player_inventory($userid);
        $ret = array();
        foreach($inventory AS $item => $details){
                if (($details['itemid']==$search || $details['verbosename']==$search) && ($details['inventorylocation']==$invloc || $invloc = "all")){
                        $ret['player']=$details;
                }
        }
        $ret['master']=iitems_get_item_details($search);
        return $ret;
}

/*
=======================================================
PLAYER HAS SUITABLE ITEM?
Does the player have an iitem that has a suitable property?
Returns an array of all usable items and their properties.

Example usage:
Player has a Chainsaw with woodcutting ability of 8.  Standard woodcutting ability of a Chainsaw is 10 - the player's Chainsaw has gotten rusty.
Player also has a hacksaw with woodcutting ability of 2.

iitems_has_property("woodcutting",8);
returns chainsaw iitem.

iitems_has_property("woodcutting",9);
returns false.

iitems_has_property("woodcutting");
returns chainsaw iitem.
returns hacksaw iitem.

iitems_has_property("woodcutting",9,true);
returns chainsaw item (third parameter, if set to true, checks only the master database iitem and not the player's iitem)

=======================================================
*/


function iitems_has_property($property,$val=1,$checkmaster=false,$inventory=false,$userid=false,$invloc="main"){
        global $session;
        if ($userid === false) $userid = $session['user']['acctid'];
        //get item info and player inventory
        if (!is_array($inventory)) $inventory = iitems_get_player_inventory($userid);

        $return = array();

        foreach($inventory AS $item => $details){
                if (!$checkmaster && $details[$property]>=$val && ($details['inventorylocation']==$invloc || $invloc=="all")){
                        $return[$item]=$inventory[$item];
                }
                if ($checkmaster){
                        $master = iitems_get_item_details($details['itemid']);
                        if ($master[$property]>=$val){
                                $return[$item]=$inventory[$item];
                        }
                }
        }
       
        if (count($return)){
                return $return;
        } else {
                return false;
        }
}


/*
=======================================================
USE ITEM
If an itemid is entered, this function uses the first item in the player's Inventory array that matches the itemid of the database master item.  Useful for 'normal' type items, where it doesn't matter which instance of an item is used.
If an array key is entered, this function uses that particular item in the player's Inventory array.  Useful for when a player will, for example, favour his shiniest throwing knife.
If modules hooking in to iitems-use-item add the "destroynow" parameter to their $args['player'] array, the item will be removed.
If modules hooking in to iitems-use-item add the "break_use_operation" parameter to their $args['player'] array, the operation will break and the item will be returned to the player.
=======================================================
*/


function iitems_use_item($item,$userid=false,$invloc){
        global $session;
        if ($userid === false) $userid = $session['user']['acctid'];
       
        $inventory = iitems_get_player_inventory($userid);
        //debug($inventory);
       
        //Does the player have the item?
        $inventorykey = iitems_has_item($item, $inventory, $userid, $invloc);

        if ($inventorykey === false){
                return false;
        } else {
                //The player has the item
                $item = array();
                $item['player'] = $inventory[$inventorykey];
                $item['player']['inv_key'] = $inventorykey;
                $item['master'] = iitems_get_item_details($item['player']['itemid']);
                $item['inventory'] = $inventory;
               
                $item = modulehook("iitems-use-item",$item);
                if ($item['player']['break_use_operation']){
                        return false;
                }
                $inventory = $item['inventory'];
               
                //Output text - modified text if inserted by hooking modules, standard text if not.
                if ($item['player']['usetext']){
                        output("%s`n`n",stripslashes($item['player']['usetext']));
                } else if ($item['master']['usetext']){
                        output("%s`n`n",stripslashes($item['master']['usetext']));
                }
               
                //Handle destroying, using up or otherwise removing items
                if ($item['player']['destroynow'] || $item['master']['destroyafteruse']){
                        if ($item['player']['quantity']<=1){
                                unset($inventory[$inventorykey]);
                        } else {
                                $inventory[$inventorykey]['quantity']--;
                        }
                } else {
                        $inventory[$inventorykey] = $item['player'];
                }
               
                $item = modulehook("iitems-use-item-after",$item);
               
                //debug($inventory);
                set_module_pref("items", serialize($inventory), "iitems", $userid);
        }
}

/*
=======================================================
DISCARD ITEM
Supply an inventory key, and this will discard that item.
If the item to be discarded is an Inventory item, other items that were occupying its space will be unceremoniously dumped into the "main" inventory.
Alternatively, supply an iitem id and this will indiscriminately discard the first iitem it comes across of that type.
=======================================================
*/


function iitems_discard_item($key,$invloc="main"){
        global $session;
        debug("Discarding item key ".$key);
        $inventory = iitems_get_player_inventory();
       
        if (is_numeric($key)){
                $loc = $inventory[$key]['inventorylocation'];
        } else {
                debug("Not numeric");
                $key = iitems_has_item($key,$inventory,false,$invloc);
                debug("Key is ".$key);
        }
        $type = $inventory[$key]['type'];
        if ($type=="inventory"){
                debug("Discarding inventory item");
                foreach ($inventory AS $item => $details){
                        if ($details['inventorylocation'] == $loc){
                                $details['inventorylocation'] = "main";
                        }
                }
        }
        if ($inventory[$key]['quantity'] > 1){
                        //debug("Lowering Quantity");
                        $inventory[$key]['quantity']--;
                } else {
                        //debug("Unsetting Key");
                        unset($inventory[$key]);
                }
       
        set_module_pref("items", serialize($inventory), "iitems");
}

/*
=======================================================
DISCARD ALL ITEMS
Supply an inventory key, and this will discard all of that item.
If the item to be discarded is an Inventory item, other items that were occupying its space will be unceremoniously dumped into the "main" inventory.
Returns quantity of items discarded.
=======================================================
*/


function iitems_discard_all_items($key){
        global $session;
        debug("Discarding item");
       
        $inventory = iitems_get_player_inventory();
        $loc = $inventory[$key]['inventorylocation'];
        $type = $inventory[$key]['type'];
       
        if (is_numeric($key)){
                $loc = $inventory[$key]['inventorylocation'];
                $type = $inventory[$key]['type'];
                if ($type=="inventory"){
                        debug("Discarding inventory item");
                        foreach ($inventory AS $item => $details){
                                if ($details['inventorylocation'] == $loc){
                                        $details['inventorylocation'] = "main";
                                }
                        }
                }
        } else {
                $key = iitems_has_item($key);
        }
       
        $qty = $inventory[$key]['quantity'];
        unset($inventory[$key]);

        set_module_pref("items", serialize($inventory), "iitems");
        return $qty;
}

/*
=======================================================
SHOW FIGHT ITEMS
Gives navs for items usable in forest fights.  To keep things simple, for now at least, we're going to assume that you can't use iitems in PVP, Master or Graveyard fights.
If the "fightinventory" module setting is true, then the player will only be able to use items with ['inventorylocation'] set to "fight", IE they have moved the item into their bandolier, utility belt, holster or similar - if not, all items with ['fightnav'] set will be allowed.
=======================================================
*/


function iitems_show_fight_items($script){
        global $session;

        $inventory = iitems_get_player_inventory();
       
        foreach ($inventory AS $item => $details){
                if ($details['fightnav']){
                        if ($details['inventorylocation']=="fight" || !(get_module_setting("fightinventory","iitems"))){
                                if ($details['quantity']){
                                        addnav("Use Items");
                                        addnav(array("Use %s (%s left)",$details['verbosename'],$details['quantity']),$script."op=fight&skill=iitems&item=$item", true);
                                } else {
                                        addnav("Use Items");
                                        if ($details['instance']){
                                                addnav(array("Use %s %s",$details['verbosename'],$details['instance']),$script."op=fight&skill=iitems&item=$item", true);
                                        } else {
                                                addnav(array("Use %s",$details['verbosename']),$script."op=fight&skill=iitems&item=$item", true);
                                        }
                                }
                        }
                }
        }
}

/*
=======================================================
TRANSFER ITEM
Transfers an item from the "main" inventory (IE a backpack or rucksack) to a different Inventory owned by the same player.  For example, transfer an item from the backpack to the bandolier, to be used in forest fights - or transfer an item into a safe box in a dwelling.
=======================================================
*/


function iitems_transfer_item($key,$transferto){
        global $session;
        $inventory = iitems_get_player_inventory();
        $central = iitems_get_item_details($inventory[$key]['itemid']);
       
        if ($central['type']=="normal"){
                $newid = iitems_has_item($inventory[$key]['itemid'],$newinventory,false,$transferto);
                if ($newid || $newid===0){
                        $inventory[$newid]['quantity'] += 1;
                } else {
                        $newitem = $inventory[$key];
                        $newitem['quantity'] = 1;
                        $newitem['inventorylocation']=$transferto;
                        $inventory[]=$newitem;
                }
                $inventory[$key]['quantity'] -= 1;
                if ($inventory[$key]['quantity'] <= 0){
                        unset($inventory[$key]);
                }
        }
        else {
                $inventory[$key]['inventorylocation'] = $transferto;
        }
        set_module_pref("items", serialize($inventory), "iitems");
}

/*
=======================================================
SHOW INVENTORY
Shows the player's inventory, with descriptions of each item.
Gives a link to use items that can be used in this area.
Gives a link to discard items that are discardable.
Built-in functionality allows to transfer items to the bandolier for use in forest fights, if the "fightinventory" modulesetting is true and the player has a bandolier item (IE 'type'=inventory, 'wlimit_capacity' is set, 'inventorylocation'=fight).
=======================================================
*/


function iitems_show_inventory($from, $loc="main", $userid=false){
        global $session, $masteriitems;
        if ($userid === false) $userid = $session['user']['acctid'];
       
        modulehook("iitems-inventory-top");
        $inventory = iitems_get_player_inventory($userid);

        //Preload master records of all iitems in player's posession
        $masteriitems = array();
        foreach($inventory AS $key => $details){
                if (!isset($masteriitems[$details['itemid']])){
                        $masteriitems[$details['itemid']]=iitems_get_item_details($details['itemid']);
                }
        }
       
        //Obtain list of carrier iitems
        $carriers = array();
        foreach($inventory AS $key => $details){
                if ($masteriitems[$details['itemid']]['type']=="inventory"){
                        $carriers[$key]=$details;
                }
        }
        //debug($carriers);
       
        //display carrier
        rawoutput("<table width=100% style='border: dotted 1px #000000'><tr><td>");
        $central = $masteriitems[$inventory[$loc]['itemid']];
        if ($central['image']) rawoutput("<table width=100% cellpadding=0 cellspacing=0><tr><td>");
        output("`b%s`b`n",$inventory[$loc]['verbosename']);
        output("%s`n",stripslashes($central['description']));
        $hookitem = array();
        $hookitem['master'] = $central;
        $hookitem['player'] = $inventory[$loc];
        $hookitem['inventorykey'] = $loc;
        modulehook("iitems-inventory", $hookitem);
        if ($central['image']) rawoutput("</td><td align='right'><img src=\"images/iitems/".$central['image']."\"></td></tr></table>");
        unset($inventory[$loc]);
       
        //display items
        rawoutput("<table width=100% style='border: dotted 1px #000000; margin-left:10px; padding-right:-10px;'>");
        $classcount=1
        foreach($inventory AS $key => $details){
                if ($details['inventorylocation']==$loc && $details['type']!="inventory"){
                        $central = $masteriitems[$details['itemid']];
                        $classcount++;
                        $class=($classcount%2?"trdark":"trlight");
                        rawoutput("<tr class='$class'><td>");
                        if ($central['image']) rawoutput("<table width=100% cellpadding=0 cellspacing=0><tr><td width=100px align=center><img src=\"images/iitems/".$central['image']."\"></td><td>");
                        if (!isset($details['instance']) || $details['instance'] == 1){
                                output("`b%s`b`n",$details['verbosename']);
                        } else {
                                output("`b%s (%s)`b`n",$details['verbosename'],$details['instance']);
                        }
                        if ($details['quantity']) output("Quantity: %s`n",$details['quantity']);
                       
                        $hookitem = array();
                        $hookitem['master'] = $central;
                        $hookitem['player'] = $details;
                        $hookitem['inventorykey'] = $key;
                        $hookitem = modulehook("iitems-inventory-intercept", $hookitem);
                       
                        //if module adds the "blockuse" param to the $player array, the item cannot be used.
                        if ($hookitem['player'][$from.'hooknav'] && !$hookitem['player']['blockuse']){
                                rawoutput("<a href=\"runmodule.php?module=iitems&op=useitem&key=$key&from=$from&invloc=".$hookitem['player']['inventorylocation']."\">Use this item</a><br />");
                                addnav("","runmodule.php?module=iitems&op=useitem&key=$key&from=$from&invloc=".$hookitem['player']['inventorylocation']);
                        }
                        if (!$hookitem['master']['cannotdiscard']){
                                $itemid = $hookitem['player']['verbosename'];
                                rawoutput("<a href=\"runmodule.php?module=iitems&op=discarditem&key=$key&from=$from\">Discard this item</a><br />");
                                addnav("","runmodule.php?module=iitems&op=discarditem&key=$key&from=$from");
                        }
                        if ($hookitem['master']['fightnav'] && get_module_setting("fightinventory","iitems")){
                                //this item can be used in combat, therefore it's safe to assume we can transfer it to the bandolier.
                                if ($loc!="fight"){
                                        $transferto = "fight";
                                } else {
                                        $transferto = "main";
                                }
                                rawoutput("<a href=\"runmodule.php?module=iitems&op=transferitem&key=$key&from=$from&transferto=$transferto\">Transfer this item to your other Inventory</a><br />");
                                addnav("","runmodule.php?module=iitems&op=transferitem&key=$key&from=$from&transferto=$transferto");
                        }
                       
                        output("%s`n",stripslashes($hookitem['master']['description']));
                       
                        $finalhookitem = array();
                        $finalhookitem['master'] = $hookitem['master'];
                        $finalhookitem['player'] = $hookitem['player'];
                        $finalhookitem['inventorykey'] = $key;
                        modulehook("iitems-inventory", $finalhookitem);
                        if ($finalhookitem['master']['image']) rawoutput("</td></tr></table>");
                        rawoutput("</td></tr>");
                }
        }
        rawoutput("</table>");
        rawoutput("</td></tr></table>");
}

/*
=======================================================
SHOW INVENTORY
Shows the player's inventory, with descriptions of each item.
Gives a link to use items that can be used in this area.
Gives a link to discard items that are discardable.
Allows transfer of iitems and, if using weight limits, shows weights and prohibits transfer if hard weight limits are enforced on non-main inventories.
=======================================================
*/


function iitems_show_inventory_new($from, $userid=false){
        global $session, $masteriitems, $weightinfo;
        if ($userid === false) $userid = $session['user']['acctid'];
       
        modulehook("iitems-inventory-top");
        $inventory = iitems_get_player_inventory($userid);
        //debug($inventory);

        //Preload master records of all iitems in player's posession
        $masteriitems = array();
        foreach($inventory AS $key => $details){
                if (!isset($masteriitems[$details['itemid']])){
                        $masteriitems[$details['itemid']]=iitems_get_item_details($details['itemid']);
                }
        }
       
        //Obtain list of carrier iitems
        $carriers = array();
        foreach($inventory AS $key => $details){
                if ($masteriitems[$details['itemid']]['type']=="inventory"){
                        $carriers[$key]=iitems_get_item_details($details['itemid']);
                }
                if ($masteriitems[$details['itemid']]['inventorylocation']=="mount"){
                        $addmount = 1;
                }
        }
        //debug($carriers);
        if ($addmount){
                $carriers['mount']=array(
                        'verbosename'=>"Mount",
                        'description'=>"Here's all the equipment currently attached to your Mount, not counting saddlebags and other carrier items.",
                        'blocktransfer'=>"true", //blocks all transfer into or out of this carrier
                );
        }
        foreach($carriers AS $ckey => $cdetails){
                //display carrier
                rawoutput("<table width=100% style='border: dotted 1px #000000'><tr><td>");
                $central = $masteriitems[$inventory[$ckey]['itemid']];
                if ($central['image']) rawoutput("<table width=100% cellpadding=0 cellspacing=0><tr><td>");
                $vn = $inventory[$ckey]['verbosename'];
                if (!$vn) $vn = $carriers[$ckey]['verbosename'];
                $desc = stripslashes($central['description']);
                if (!$desc) $desc = stripslashes($carriers[$ckey]['description']);
                output("`b%s`b`n",$vn);
                output("%s`n",$desc);
                $hookitem = array();
                $hookitem['master'] = $central;
                $hookitem['player'] = $inventory[$ckey];
                $hookitem['inventorykey'] = $ckey;
                modulehook("iitems-inventory", $hookitem);
                //debug($hookitem);
                if ($hookitem['master']['image']) rawoutput("</td><td align='right'><img src=\"images/iitems/".$hookitem['master']['image']."\"></td></tr></table>");
               
                //display items
                rawoutput("<table width=100% style='border: dotted 1px #000000; margin-left:10px; padding-right:-10px;'>");
                $classcount=1
                foreach($inventory AS $key => $details){
                        if ($details['inventorylocation']==$ckey && $masteriitems[$details['itemid']]['type']!="inventory"){
                                $central = $masteriitems[$details['itemid']];
                                $classcount++;
                                $class=($classcount%2?"trdark":"trlight");
                                rawoutput("<tr class='$class'><td>");
                                if ($central['image']) rawoutput("<table width=100% cellpadding=0 cellspacing=0><tr><td width=100px align=center><img src=\"images/iitems/".$central['image']."\"></td><td>");
                                if (!isset($details['instance']) || $details['instance'] == 1){
                                        output("`b%s`b`n",stripslashes($details['verbosename']));
                                } else {
                                        output("`b%s (%s)`b`n",$details['verbosename'],$details['instance']);
                                }
                                if ($details['quantity']) output("Quantity: %s`n",$details['quantity']);
                               
                                $hookitem = array();
                                $hookitem['master'] = $central;
                                $hookitem['player'] = $details;
                                $hookitem['inventorykey'] = $key;
                                $hookitem = modulehook("iitems-inventory", $hookitem);
                                //debug($hookitem);
                               
                                //if module adds the "blockuse" param to the $player array, the item cannot be used.
                                if (($hookitem['player'][$from.'hooknav'] || $hookitem['master'][$from.'hooknav']) && !$hookitem['player']['blockuse']){
                                        rawoutput("<a href=\"runmodule.php?module=iitems&op=useitem&key=$key&from=$from&invloc=".$hookitem['player']['inventorylocation']."\">Use this item</a><br />");
                                        addnav("","runmodule.php?module=iitems&op=useitem&key=$key&from=$from&invloc=".$hookitem['player']['inventorylocation']);
                                }
                                if (!$hookitem['master']['cannotdiscard']){
                                        $itemid = $hookitem['player']['verbosename'];
                                        rawoutput("<a href=\"runmodule.php?module=iitems&op=discarditem&key=$key&from=$from\">Discard this item</a><br />");
                                        addnav("","runmodule.php?module=iitems&op=discarditem&key=$key&from=$from");
                                }
                               
                                //Evaluate potential carriers for this iitem
                                if (!$carriers[$hookitem['player']['inventorylocation']]['blocktransfer']){ //the carrier that the item is currently in doesn't restrict transfer of items
                                        foreach($carriers AS $ckey1 => $cdetails1){
                                                if (!$cdetails1['blocktransfer'] || $hookitem['master']['allowtransfer']==$ckey1){ //the carrier to be evaluated does not restrict transfer of iitems in OR the item is excluded
                                                        if ($hookitem['player']['inventorylocation']!=$ckey1){ //the iitem is not already in the carrier we're evaluating
                                                                if (!$hookitem['master']['blockcarrier_'.$ckey1]){ //the iitem is not blocked from being in this carrier
                                                                        if (!$hookitem['master']['blocktransfer'] || $hookitem['master']['allowtransfer']==$ckey1){ //the iitem is not blocked from being transferred completely OR the iitem is allowed to be transferred to only this carrier
                                                                                if ($ckey1=="fight" && !$hookitem['master']['fightnav']) continue; //skip if we're looking at the fight carrier and this thing can't go in fights
                                                                                $cvname = $cdetails1['verbosename'];
                                                                                $transferto = $ckey1;
                                                                                //check hard weight limits
                                                                                //debug($weightinfo);
                                                                                if ($weightinfo[$ckey1]['wlimit_hardlimit']){
                                                                                        //debug($ckey1." has hard weight limit");
                                                                                        //check weight
                                                                                        if ($weightinfo[$ckey1]['max'] < ($weightinfo[$ckey1]['current'] + $hookitem['player']['weight'])){
                                                                                                //rawoutput("This item won't fit in your $cvname<br />");
                                                                                                continue;
                                                                                        }
                                                                                }
                                                                                rawoutput("<a href=\"runmodule.php?module=iitems&op=transferitem&key=$key&from=$from&transferto=$transferto\">Transfer this item to your $cvname</a><br />");
                                                                                addnav("","runmodule.php?module=iitems&op=transferitem&key=$key&from=$from&transferto=$transferto");
                                                                                //debug("Item can be transferred to ".$transferto);
                                                                        }
                                                                }
                                                        }
                                                }
                                        }
                                }
                               
                                output("%s`n",stripslashes($hookitem['master']['description']));
                               
                                $finalhookitem = array();
                                $finalhookitem['master'] = $hookitem['master'];
                                $finalhookitem['player'] = $hookitem['player'];
                                $finalhookitem['inventorykey'] = $key;
                                //modulehook("iitems-inventory", $finalhookitem);
                                if ($finalhookitem['master']['image']) rawoutput("</td></tr></table>");
                                rawoutput("</td></tr>");
                        }
                }
                rawoutput("</table>");
                rawoutput("</td></tr></table><br />");
        }
}

?>
 


Also look at my notes on a potential new system, but here's the questions we face: How do we handle it if one day I decide to make a five-shot teleporter?

DragonBones Item System

Player experience
Player clicks on their inventory, is presented with options of two views - Expanded or Compact ("expanded" is the legacy view). Compact view shows them all their iitems as small icons, one icon per item (this will mean that if they have two hundred pieces of wood, they see two hundred wood icons), within their backpacks or bandoliers or what-have-you. Mouseovers on each of them show the full view a la the old system. This will suck for mobile phones, come up with something better. :-/

The idea behind the new item system is that, as before, any stat can be assigned to any item - but this time, it'll be SIMPLER.

(added feature - allow ANY item to be put in a gift box and delivered at newday, for one supporter point or x Requisition per gift box)
(some items exempt from needing the gift box and delay, can be traded as normal, IE cards)

For example, method of making a grenade usable in combat by transferring it from backpack to bandolier:
In the Inventory screen, have the "transfer" link reference the first available itemid that exists to transfer (let's say 42). So, inventory.php?op=move&item=42.
If httpget move, then set_item_pref("location","bandolier",httpget(item)).


FUNCTIONS:

set_item_pref(setting, value, itemid);
Sets an item's pref, for example its location, image, hitpoints etc.

give_item(item,player,prefs=false);
Gives an item to a player. If the prefs flag is set, then those prefs are copied into the player items table. If no prefs are set, then unless the item has prefs that specifically override settings in the master table, then the item will be identical to its master item.

delete_item(item/itemid);
Simply delete that item from the table.

show_inventory(playerid,location=false);
Any items that aren't the same as other items of the same type are shown separately, eliminating the need for distinctions between Normal and Special items, which was only in there so the array wouldn't overflow anyway.

has_item(item, locations=false, exclude=false);
If locations is an array of acceptable locations, then only items from those locations will be shown (if exclude is set, then only items that aren't in those locations will be shown). Otherwise, this returns just true or false.

get_item(item/itemid, locations=false, exclude=false);
As above, but returns a full item array of prefs and so forth. Returns the first item found, unless item is numeric, in which case it returns that one.

get_all_items(item, locations=false, exclude=false);
As above, but returns an array containing all of the searched-for items and their prefs.

compare_items(item, locations=false, exclude=false);
Compares all items of the same type and returns an array of those items who are not stock (IE lower hitpoints, more rust, enhanced) if those items are found - or false if they're not found.

get_equipped_item(location);
Get the item or items equipped to a certain location (left hand, right hand, head, bandolier, etc). Returns an array of the items and their prefs.

get_items_with_properties(properties (can be a string or an array), master=false);
Returns all the player's items with the properties described in $properties, or all of the master items with $properties if master is true.

use_item(array of item and location OR specific itemid OR just item (assume "main" inventory unless it's found elsewhere));
Sends the item through hooks and then performs actions based on those hooks, just like now, only with about ten times less code.


PROBLEMS
What about when a player wants to cut his grass, and has two scythes, one of which is a little rustier? Should we give an option to use either, or both?
Have the player EQUIP the item in question first?
Does this even apply to consumable items, not just equippables?
How about a five-shot teleporter. Design around THAT, smartass.
Okay, so in the Inventory screen, display the unused items and then the used or "special" items that have different prefs to the master items. Compare them. Maybe.


 
Profile Email Website
Quote
Anonymous: Temper
 Monday, August 30 2010 @ 08:18 AM UTC  


I say take your time making sure we won't all die in a future Cataclysm. We can amuse ourselves in the meantime. Because you've done beautiful work so far, and I would be very sad if that feel apart while you did more awesome things. Anyways, I guess that but was me meandering towards a "You are amazing sir, and here's a.. Err.. A danish of some sort. Because that's all I have on me at the moment" statement.


 
Quote
Anonymous: Temper
 Monday, August 30 2010 @ 08:22 AM UTC  


In other news, I really should log on before posting these things, so I can edit them, and not look like a complete idiot. But my sentiment still stands. You do amazing work.


 
Quote
Hairy Mary
 Monday, August 30 2010 @ 10:06 AM UTC  
Forum Improbable Badass
Improbable Badass

Status: offline

Registered: 08/17/08
Posts: 1083

Yep. Agree with everyone here. What I suspect would happen if you left it as it is, is that it would get more and more annoying until you did change it, and the longer you left it, the worse it would get, and the more other things you'd have to mess with to fit into the new system, and the greater chance of something unforeseen and unpleasent happening when you do.

You're probably better off biting the bullet now, and getting and doing it. Then you can forget about it.

On the other hand, of you've just been grinding away, and you were looking forward to doing something more fun, then it might not be a bad idea to do a few such things first before going back to it. A bit of a break and all that.

But then, what do I really know about such things? Well I do know that we'll all carry happily on whatever.


 
Profile Email
Quote
Ashtu
 Monday, August 30 2010 @ 12:46 PM UTC  
Forum Improbable Badass
Improbable Badass

Status: offline

Registered: 11/04/09
Posts: 440

Quote by: Harris

In all levity AND seriousness- take your time. We'll amuse ourselves. I guarantee it.


Dan, a cynical view from a fellow (but too often too cynical) sysadmin -

Cataclysms / catastrophic failures / data loss / etc - the things YOU KNOW (in retrospect) you could have avoided, if you had been more careful -
The beautiful, elegant programing solutions that nobody else understands, but make you feel like a ten-foot-tall fertility god - until you forget to initialize that variable when you take it live -
The screaming rants that sometimes actually slip out in fits of madness ("dammit, you blithering f**king idiots, HOW MANY TIMES do I have to tell you MS Word is NOT a text editor!!") -

In a corporate world, these things would lose you your job.
In this world, all you lose is the jerks and lusers. (and a little more hair)

The real people - the ones who may not understand programing, the ones who put relationships above self-gratification, the ones who will laugh at themselves before they laugh at others -

We stick around. We love each other. And you.

That's worth an occasional "aw, SHIT!", ain't it?


Thank you.
 
Profile Email
Quote
crashtestpilot
 Monday, August 30 2010 @ 02:45 PM UTC  
Forum Improbable Badass
Improbable Badass

Status: offline

Registered: 10/29/08
Posts: 351

First, let me echo the gallons of tea and sympathy: Wow, what a conundrum; we totally trust you; it's kind of why were here, and not at some other game; and yes, build it strong and solid.

Now let me ask a question: What does it mean exactly when you say "let you lot go on without new content?"

Does that mean Dwellings are frozen? What exactly is included in the set of "new content" from your perspective?

From a product manager's perspective, "if it's not scalable, don't do it," is a maxim I've lived (and died) by.

My overarching point is that it's your world, your game -- we just live & die in it.

I think the real challenge is doing what you can to prevent Cataclysm-like events.
And on that subject, you know best.

From a player's perspective (this player) the Hunter's Lodge was a nice-to-have; the Dwellings unit was a great-to-have; and the Sheds (after the Dwellings were underway) was a must-have.

But the Island continues to turn, your core players aren't going elsewhere, and we're muddling through as best we can.
And when it's all fixed, you'll have a scalable system that won't break, and can accommodate more players. Which ultimately will help to put lager on the table, and that's what counts.

Just my two cents, take them fro what they're worth,

Yours,

~Crash


 
Profile Email
Quote
Devin
 Monday, August 30 2010 @ 03:48 PM UTC  
Forum Contestant
Contestant

Status: offline

Registered: 06/17/10
Posts: 30

To echo the above, I'm sure very few of us have any inclination to leave because of a few crashes when such a heroic effort is obviously being made to remedy and prevent them. We understand that running a world isn't as easy as you make it look!

On a related matter, I've been thinking about this for a while, and now seems as good a time as any (particularly with myself stranded away from work on a "vacation" to take my S.O. to the in-laws) to suggest it. Do you want to add some more programming muscle to the project? I can volunteer probably multiple hours a day, and I've already familiarized myself with the Labs version of the code. No ulterior motive, I just love the place and would love to do my part to make it better. Offer's on the table.

Regardless, you're doing a great job, and I can't wait to see what comes next. Cheers!


 
Profile Email
Quote
Tyr
 Monday, August 30 2010 @ 03:55 PM UTC  
Forum Badass
Badass

Status: offline

Registered: 09/15/09
Posts: 98

Joining the chorus of "Better to fix it, the game is awesome, you're awesome, don't drive yourself crazy."


 
Profile Email
Quote
tehdave
 Monday, August 30 2010 @ 06:19 PM UTC  
Forum Improbable Badass
Improbable Badass

Status: offline

Registered: 12/17/09
Posts: 429

Quote by: Tyr

Joining the chorus of "Better to fix it, the game is awesome, you're awesome, don't drive yourself crazy."



I'll drink to that! Puts on Arbuthnot Mustache over his goatee HEAR HEAR!


Isn't sanity just a one-trick pony anyway? All you get is one trick: rational thinking. But when you're good and crazy, oooh, oooh, the sky's the limit.
 
Profile Email
Quote
Miss Hellebore
 Thursday, September 02 2010 @ 08:22 PM UTC  
Forum Contender
Contender

Status: offline

Registered: 07/03/10
Posts: 57

Quote by: Harris

In all levity AND seriousness- take your time. We'll amuse ourselves. I guarantee it.



Well, I for one won't stand for it! I have come to expe-- Oooooo! Shiny!

*chases the shiny something off of the thread.*


 
Profile Email
Quote
Cousjava
 Saturday, September 04 2010 @ 06:33 PM UTC  
Forum Improbable Badass
Improbable Badass

Status: offline

Registered: 03/13/10
Posts: 244

Quote by: Tyr

don't drive yourself crazy."



Why drive when you can take the train?


A table, a chair, a bowl of fruit and a trombone; what else does a man need to be happy?
 
Profile Email Website
Quote
Content generated in: 1.16 seconds
New Topic Post Reply



 All times are UTC. The time is now 12:05 PM.

Normal Topic Normal Topic
Locked Topic Locked Topic
Sticky Topic Sticky Topic
New Post New Post
Sticky Topic W/ New Post Sticky Topic W/ New Post
Locked Topic W/ New Post Locked Topic W/ New Post
View Anonymous Posts 
Anonymous users can post 
Filtered HTML Allowed 
Censored Content