/*
 * psitemstats.cpp
 *
 * Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) 
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation (version 2 of the License)
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */


#include <psconfig.h>

#include <csutil/randomgen.h>
#include "imesh/object.h"
#include <imesh/spritecal3d.h>
#include "iengine/mesh.h"
#include <physicallayer/propclas.h>
#include <physicallayer/entity.h>
#include <propclass/mesh.h>

#include "psglyph.h"
#include "psitem.h"
#include "psitemstats.h"
#include "util/log.h"
#include "util/psstring.h"
#include "util/serverconsole.h"
#include "../globals.h"
#include "../psserver.h"
#include "../cachemanager.h"
#include "psmerchantinfo.h"



//-----------------------------------------------------------------------------
                                      
psItemArmorStats::psItemArmorStats()
{
    armor_type = PSITEMSTATS_ARMORTYPE_NONE;
    armor_class = 0;        
    memset(damage_protection,0,sizeof(damage_protection));
    hardness = 0.0;    
}

float psItemArmorStats::Protection(PSITEMSTATS_DAMAGETYPE dmgtype)
{
    if (dmgtype <0 || dmgtype >= PSITEMSTATS_DAMAGETYPE_COUNT)
        return 0.0f;
    return damage_protection[dmgtype];    
}

void psItemArmorStats::ReadStats( iResultRow& row )
{
    hardness = row.GetFloat("armor_hardness");
    damage_protection[PSITEMSTATS_DAMAGETYPE_SLASH]     = row.GetFloat("armor_prot_slash");
    damage_protection[PSITEMSTATS_DAMAGETYPE_BLUNT]     = row.GetFloat("armor_prot_blunt");
    damage_protection[PSITEMSTATS_DAMAGETYPE_PIERCE]    = row.GetFloat("armor_prot_pierce");
    damage_protection[PSITEMSTATS_DAMAGETYPE_FORCE]     = row.GetFloat("armor_prot_force");
    damage_protection[PSITEMSTATS_DAMAGETYPE_FIRE]      = row.GetFloat("armor_prot_fire");
    damage_protection[PSITEMSTATS_DAMAGETYPE_ICE]       = row.GetFloat("armor_prot_ice");
    damage_protection[PSITEMSTATS_DAMAGETYPE_AIR]       = row.GetFloat("armor_prot_air");
    damage_protection[PSITEMSTATS_DAMAGETYPE_POISON]    = row.GetFloat("armor_prot_poison");
    damage_protection[PSITEMSTATS_DAMAGETYPE_DISEASE]   = row.GetFloat("armor_prot_disease");
    damage_protection[PSITEMSTATS_DAMAGETYPE_HOLY]      = row.GetFloat("armor_prot_holy");
    damage_protection[PSITEMSTATS_DAMAGETYPE_UNHOLY]    = row.GetFloat("armor_prot_unholy");
        
    csString temp(row["armorvsweapon_type"]);
    if ( temp.Length() == 2 )
    {
        if (temp.GetAt(0) == '1')
            armor_type = PSITEMSTATS_ARMORTYPE_LIGHT;
        else if (temp.GetAt(0) == '2')
            armor_type = PSITEMSTATS_ARMORTYPE_MEDIUM;
        else if (temp.GetAt(0) == '3')
            armor_type = PSITEMSTATS_ARMORTYPE_HEAVY;
            
        armor_class = temp.GetAt(1);            
    }
    
}
    
//-----------------------------------------------------------------------------

psItemWeaponStats::psItemWeaponStats()
{
    weapon_type=PSITEMSTATS_WEAPONTYPE_NONE;
    
    memset(weapon_skill,0,sizeof(weapon_skill));
    memset(attribute_bonuses,0,sizeof(attribute_bonuses));
    memset(damages,0,sizeof(damages));
    memset(damage_percents,0,sizeof(damage_percents));
    
    latency=0.0f;
    penetration=0.0f;
    
    untargeted_block_value=0.0f;
    targeted_block_value=0.0f;
    counter_block_value=0.0f;
}

void psItemWeaponStats::ReadStats( iResultRow& row )
{
    csString type( row["item_type"] );
    if ( type == "SWORD" )
        weapon_type = PSITEMSTATS_WEAPONTYPE_SWORD;
    else if ( type == "AXE" )
        weapon_type = PSITEMSTATS_WEAPONTYPE_AXE;
    else if ( type == "DAGGER" )
        weapon_type = PSITEMSTATS_WEAPONTYPE_DAGGER;
    else if ( type == "HAMMER" )
        weapon_type = PSITEMSTATS_WEAPONTYPE_HAMMER;
    else if ( type == "BOW" )
        weapon_type = PSITEMSTATS_WEAPONTYPE_BOW;
    else if ( type == "CROSSBOW" )
        weapon_type = PSITEMSTATS_WEAPONTYPE_CROSSBOW;
    else if ( type == "SLING" )
        weapon_type = PSITEMSTATS_WEAPONTYPE_SLING;

    weapon_skill[PSITEMSTATS_WEAPONSKILL_INDEX_0] = CacheManager::GetSingleton().ConvertSkill(row.GetInt("item_skill_id_1"));
    weapon_skill[PSITEMSTATS_WEAPONSKILL_INDEX_1] = CacheManager::GetSingleton().ConvertSkill(row.GetInt("item_skill_id_2"));
    weapon_skill[PSITEMSTATS_WEAPONSKILL_INDEX_2] = CacheManager::GetSingleton().ConvertSkill(row.GetInt("item_skill_id_3"));
    
    latency = row.GetFloat("weapon_speed");

    damages[PSITEMSTATS_DAMAGETYPE_SLASH] = row.GetFloat("weapon_dmg_slash");
    damages[PSITEMSTATS_DAMAGETYPE_BLUNT] = row.GetFloat("weapon_dmg_blunt");
    damages[PSITEMSTATS_DAMAGETYPE_PIERCE] = row.GetFloat("weapon_dmg_pierce");
    damages[PSITEMSTATS_DAMAGETYPE_FORCE] = row.GetFloat("weapon_dmg_force");
    damages[PSITEMSTATS_DAMAGETYPE_FIRE] = row.GetFloat("weapon_dmg_fire");
    damages[PSITEMSTATS_DAMAGETYPE_ICE] = row.GetFloat("weapon_dmg_ice");
    damages[PSITEMSTATS_DAMAGETYPE_AIR] = row.GetFloat("weapon_dmg_air");
    damages[PSITEMSTATS_DAMAGETYPE_POISON] = row.GetFloat("weapon_dmg_poison");
    damages[PSITEMSTATS_DAMAGETYPE_DISEASE] = row.GetFloat("weapon_dmg_disease");
    damages[PSITEMSTATS_DAMAGETYPE_HOLY] = row.GetFloat("weapon_dmg_holy");
    damages[PSITEMSTATS_DAMAGETYPE_UNHOLY] = row.GetFloat("weapon_dmg_unholy");

    damage_percents[PSITEMSTATS_DAMAGETYPE_SLASH] = row.GetFloat("weapon_dmg_pct_slash");
    damage_percents[PSITEMSTATS_DAMAGETYPE_BLUNT] = row.GetFloat("weapon_dmg_pct_blunt");
    damage_percents[PSITEMSTATS_DAMAGETYPE_PIERCE] = row.GetFloat("weapon_dmg_pct_pierce");
    damage_percents[PSITEMSTATS_DAMAGETYPE_FORCE] = row.GetFloat("weapon_dmg_pct_force");
    damage_percents[PSITEMSTATS_DAMAGETYPE_FIRE] = row.GetFloat("weapon_dmg_pct_fire");
    damage_percents[PSITEMSTATS_DAMAGETYPE_ICE] = row.GetFloat("weapon_dmg_pct_ice");
    damage_percents[PSITEMSTATS_DAMAGETYPE_AIR] = row.GetFloat("weapon_dmg_pct_air");
    damage_percents[PSITEMSTATS_DAMAGETYPE_POISON] = row.GetFloat("weapon_dmg_pct_poison");
    damage_percents[PSITEMSTATS_DAMAGETYPE_DISEASE] = row.GetFloat("weapon_dmg_pct_disease");
    damage_percents[PSITEMSTATS_DAMAGETYPE_HOLY] = row.GetFloat("weapon_dmg_pct_holy");
    damage_percents[PSITEMSTATS_DAMAGETYPE_UNHOLY] = row.GetFloat("weapon_dmg_pct_unholy");
                   
    penetration = row.GetFloat("weapon_penetration");
    targeted_block_value = row.GetFloat("weapon_block_targeted");
    untargeted_block_value = row.GetFloat("weapon_block_untargeted");
    counter_block_value = row.GetFloat("weapon_counterblock");               
}

PSSKILL psItemWeaponStats::Skill(PSITEMSTATS_WEAPONSKILL_INDEX index)
{
    if (index < 0 || index >= PSITEMSTATS_WEAPONSKILL_INDEX_COUNT)
        return PSSKILL_NONE;
    return weapon_skill[index];
}

PSITEMSTATS_STAT psItemWeaponStats::AttributeBonusType(PSITEMSTATS_STAT_BONUS_INDEX index)
{
    if (index<0 || index >= PSITEMSTATS_STAT_BONUS_INDEX_COUNT)
        return PSITEMSTATS_STAT_NONE;
    return attribute_bonuses[index].attribute_id;
}

float psItemWeaponStats::AttributeBonusMax(PSITEMSTATS_STAT_BONUS_INDEX index)
{
    if (index<0 || index >= PSITEMSTATS_STAT_BONUS_INDEX_COUNT)
        return 0.0f;
    return attribute_bonuses[index].bonus_max;
}

float psItemWeaponStats::Damage(PSITEMSTATS_DAMAGETYPE dmgtype)
{
    if (dmgtype<0 || dmgtype >= PSITEMSTATS_DAMAGETYPE_COUNT)
        return 0.0f;
    return damages[dmgtype];
}


float psItemWeaponStats::ExtraDamagePercent(PSITEMSTATS_DAMAGETYPE dmgtype)
{
    if (dmgtype < 0 || dmgtype >= PSITEMSTATS_DAMAGETYPE_COUNT)
        return 0.0f;
    return damage_percents[dmgtype];
}

//-----------------------------------------------------------------------------
psItemAmmoStats::psItemAmmoStats()
{
    ammunition_type = PSITEMSTATS_AMMOTYPE_NONE;
}

psItemAmmoStats::~psItemAmmoStats()
{
}

void psItemAmmoStats::ReadStats( iResultRow& row )
{
    csString type( row["item_type"] );
    if ( type == "ARROW" )
        ammunition_type = PSITEMSTATS_AMMOTYPE_ARROWS;
    else if ( type == "BOLT" )
        ammunition_type = PSITEMSTATS_AMMOTYPE_BOLTS;
    else if ( type == "ROCK" )
        ammunition_type = PSITEMSTATS_AMMOTYPE_ROCKS;
}

//-----------------------------------------------------------------------------
    

//-----------------------------------------------------------------------------

// Definition of the itempool for psItemStats
PoolAllocator<psItemStats> psItemStats::itempool;

void *psItemStats::operator new(size_t allocSize)
{
    CS_ASSERT(allocSize<=sizeof(psItemStats));
    return (void *)itempool.CallFromNew();
}

void psItemStats::operator delete(void *releasePtr)
{
    itempool.CallFromDelete((psItemStats *)releasePtr);
}


psItemStats::psItemStats()
{
    uid=0;
    name = "";
    description="";
    weight=0.0f;
    size=0;
    container_max_size=0;

    valid_slots=0x00000000;
    decay_rate=0;
    flags=0x00000000;

    mesh_name=NULL;
    texture_name.Clear();
    texturepart_name.Clear();
    image_name=NULL;
    
    visible_distance = DEF_PROX_DIST;
}

psItemStats::~psItemStats()
{
}

bool psItemStats::ReadItemStats(iResultRow& row)
{
    uid = row.GetUInt32("id");
    name = row["name"];

    SetDescription(row["description"]);

    SetProgressionEventEquip( row["prg_evt_equip"] );
    SetProgressionEventUnEquip( row["prg_evt_unequip"] );

    item_quality       = row.GetFloat("item_max_quality");    
    weight             = row.GetFloat("weight");
    size               = (unsigned short)row.GetInt("size");
    container_max_size = (unsigned short)row.GetInt("container_max_size");                 
    visible_distance   = row.GetFloat("visible_distance");
    decay_rate         = row.GetFloat("decay_rate");  
    
    // Load in the valid slots for the item.
    LoadSlots( row );
        
    ParseFlags( row );
            
    if (flags==0) // no type specified is illegal
    {
        Error2("Item %s has no valid flags specified.",row["id"] );
        return false;
    }

    SetArmorVsWeaponType(row["armorvsweapon_type"]);
    
    weaponStats.ReadStats( row );        
    armorStats.ReadStats( row );
    ammoStats.ReadStats( row );
    
    // Mesh, Texture, Part and Image strings
    const char *meshname = CacheManager::GetSingleton().FindCommonString(row.GetUInt32("cstr_id_gfx_mesh"));
    if (row.GetUInt32("cstr_id_gfx_mesh")!=0 && !meshname) {
      Error3("Error, no commonstring exists with number %u while loading item_stats.id %u.\n",row.GetUInt32("cstr_id_gfx_mesh"),uid);
      return false;
    } else
      SetMeshName(meshname);

    const char *texturename = CacheManager::GetSingleton().FindCommonString(row.GetUInt32("cstr_id_gfx_texture"));
    if (row.GetUInt32("cstr_id_gfx_texture")!=0 && !texturename) {
      Error3("Error, no commonstring exists with number %u while loading item_stats.id %u.\n",row.GetUInt32("cstr_id_gfx_texture"),uid);
      return false;
    } else
      SetTextureName(texturename);

    SetPartName(CacheManager::GetSingleton().FindCommonString(row.GetUInt32("cstr_id_part")));
    SetImageName(CacheManager::GetSingleton().FindCommonString(row.GetUInt32("cstr_id_gfx_icon")));
    SetPartMeshName(CacheManager::GetSingleton().FindCommonString(row.GetUInt32("cstr_id_part_mesh")));
    SetPrice(row.GetInt("base_sale_price"));
    SetCategory(CacheManager::GetSingleton().GetItemCategoryByID(row.GetInt("category_id")));

    reqs[0].name = row["requirement_1_name"];
    reqs[0].min_value = row.GetFloat("requirement_1_value");
    reqs[1].name = row["requirement_2_name"];
    reqs[1].min_value = row.GetFloat("requirement_2_value");
    reqs[2].name = row["requirement_3_name"];
    reqs[2].min_value = row.GetFloat("requirement_3_value");

    ammo_item_type_id        = row.GetInt("item_type_id_ammo");
    spell_id_on_hit          = row.GetInt("spell_id_on_hit");
    spell_on_hit_probability = row.GetFloat("spell_on_hit_prob");
    spell_id_feature         = row.GetInt("spell_id_feature");
    spell_feature_charges    = row.GetInt("spell_feature_charges");
    spell_feature_timing     = row.GetInt("spell_feature_timing");

    SetSound(row["sound"]);

    int anim_list_id = row.GetInt("item_anim_id");
    if (anim_list_id)
    {
        anim_list = CacheManager::GetSingleton().FindAnimationList(anim_list_id);
        if (!anim_list)
            return false;
    }
    else
        anim_list = NULL;

    if (row["illum_definition"])
    {
        SetIllumination(row["illum_definition"]);
    }
    return true;
}


void psItemStats::ParseFlags( iResultRow& row )
{
    psString flagstr(row["flags"]);
    
    int z = 0;
    // Iterates over all the flags in the list and adds that flag to the value if it
    // has been found in the flag string from the database. 
    while ( CacheManager::GetSingleton().ItemStatFlagArray[z].string != "END" )
    {
        if (flagstr.FindSubString(CacheManager::GetSingleton().ItemStatFlagArray[z].string,0,true)!=-1)
        {
            flags |= CacheManager::GetSingleton().ItemStatFlagArray[z].flag;
        }
        z++;
    }               
}


void psItemStats::LoadSlots( iResultRow& row )
{
    // Valid slots
    psString slots(row["valid_slots"]);

    if (slots.FindSubString("BULK",0,true)!=-1)
    {
        valid_slots|=PSITEMSTATS_SLOT_BULK;
    } 
    if (slots.FindSubString("RIGHTHAND",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_RIGHTHAND);
        valid_slots|=PSITEMSTATS_SLOT_RIGHTHAND;
    }        
    if (slots.FindSubString("LEFTHAND",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_LEFTHAND);
        valid_slots|=PSITEMSTATS_SLOT_LEFTHAND;
    }
    if (slots.FindSubString("BOTHHANDS",0,true)!=-1)
    {
        valid_slots|=PSITEMSTATS_SLOT_BOTHHANDS;
        valid_slots_array.Push(PSCHARACTER_SLOT_BOTHHANDS);
    }            
    if (slots.FindSubString("HEAD",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_HEAD);
        valid_slots|=PSITEMSTATS_SLOT_HEAD;
    } 
    if (slots.FindSubString("LEFTFINGER",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_LEFTFINGER);
        valid_slots|=PSITEMSTATS_SLOT_LEFTFINGER;
    }
    if (slots.FindSubString("RIGHTFINGER",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_RIGHTFINGER);
        valid_slots|=PSITEMSTATS_SLOT_RIGHTFINGER;
    }
    if (slots.FindSubString("NECK",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_NECK);
        valid_slots|=PSITEMSTATS_SLOT_NECK;
    }        
    if (slots.FindSubString("BACK",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_BACK);
        valid_slots|=PSITEMSTATS_SLOT_BACK;
    }        
    if (slots.FindSubString("ARMS",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_ARMS);
        valid_slots|=PSITEMSTATS_SLOT_ARMS;
    }                
    if (slots.FindSubString("GLOVES",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_GLOVES);
        valid_slots|=PSITEMSTATS_SLOT_GLOVES;
    }        
    if (slots.FindSubString("BOOTS",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_BOOTS);
        valid_slots|=PSITEMSTATS_SLOT_BOOTS;
    }        
    if (slots.FindSubString("LEGS",0,true)!=-1)
    {
        valid_slots|=PSITEMSTATS_SLOT_LEGS;
        valid_slots_array.Push(PSCHARACTER_SLOT_LEGS);
    }        
    if (slots.FindSubString("BELT",0,true)!=-1)
    {
        valid_slots|=PSITEMSTATS_SLOT_BELT;
        valid_slots_array.Push(PSCHARACTER_SLOT_BELT);
    }        
    if (slots.FindSubString("BRACERS",0,true)!=-1)
    {
        valid_slots_array.Push(PSITEMSTATS_SLOT_BRACERS);
        valid_slots|=PSITEMSTATS_SLOT_BRACERS;
    }        
    if (slots.FindSubString("TORSO",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_TORSO);
        valid_slots|=PSITEMSTATS_SLOT_TORSO;
    }        
    if (slots.FindSubString("MIND",0,true)!=-1)
    {
        valid_slots_array.Push(PSCHARACTER_SLOT_MIND);
        valid_slots|=PSITEMSTATS_SLOT_MIND; 
    }
    if (slots.FindSubString("BANK",0,true)!=-1)
    {
        valid_slots|=PSITEMSTATS_SLOT_BANK;
    }        
    if (slots.FindSubString("CRYSTAL",0,true)!=-1)
    {
        valid_slots|=PSITEMSTATS_SLOT_CRYSTAL;
    }
    if (slots.FindSubString("AZURE",0,true)!=-1)
    {    
        valid_slots|=PSITEMSTATS_SLOT_AZURE;
    }        
    if (slots.FindSubString("RED",0,true)!=-1)
    {
        valid_slots|=PSITEMSTATS_SLOT_RED;
    }        
    if (slots.FindSubString("DARK",0,true)!=-1)
    {    
        valid_slots|=PSITEMSTATS_SLOT_DARK;
    }        
    if (slots.FindSubString("BROWN",0,true)!=-1)
    {
        valid_slots|=PSITEMSTATS_SLOT_BROWN;
    }        
    if (slots.FindSubString("BLUE",0,true)!=-1)
    {
        valid_slots|=PSITEMSTATS_SLOT_BLUE;
    }        
}

// Saves current Item Stat values back to the DB.
bool psItemStats::Save()
{
    psStringArray fields;
    bool update_ok=true;

    // Existing Item, update
    const char *fieldnames[]= {
      "name", "weight", "visible_distance", "size", "container_max_size", "decay_rate", //"valid_slots",  "flags",
            "category_id", "base_sale_price", "item_type_id_ammo", "item_anim_id", "description",
            "weapon_block_targeted", "weapon_block_untargeted", "weapon_counterblock", "weapon_speed",
            "weapon_dmg_slash", "weapon_dmg_blunt", "weapon_dmg_pierce", "weapon_dmg_force", "weapon_dmg_fire", "weapon_dmg_ice", 
            "weapon_dmg_air", "weapon_dmg_poison", "weapon_dmg_disease", "weapon_dmg_holy", "weapon_dmg_unholy", 
            "armor_prot_slash", "armor_prot_blunt", "armor_prot_pierce", "armor_prot_force", "armor_prot_fire", "armor_prot_ice", 
            "armor_prot_air", "armor_prot_poison", "armor_prot_disease", "armor_prot_holy", "armor_prot_unholy", 
            "cstr_id_gfx_mesh", 
            "requirement_1_name", "requirement_1_value", "requirement_2_name", "requirement_2_value", "requirement_3_name", "requirement_3_value",
            "prg_evt_equip", "prg_evt_unequip"
    };

    // Owning character ID
    // INT        fields.FormatPush("%u",
    // FLOAT    fields.FormatPush("%1.2f",
    // STRING    fields.Push(

    fields.Push( this->name );                              // name
    fields.FormatPush( "%1.2f", this->weight );             // weight
    fields.FormatPush( "%1.2f", this->visible_distance );   // visible_distance
    fields.FormatPush( "%u", this->size );                  // size
    fields.FormatPush( "%u", this->container_max_size );    // contianer_max_size
    fields.FormatPush( "%1.2f", this->decay_rate );            // decay_rate
    //fields.FormatPush( "%u", this->valid_slots );         // valid_slots
    //fields.FormatPush( "%u", this->flags );               // flags
    fields.FormatPush( "%u", this->category->id );          // category_id
    fields.FormatPush( "%u", this->price.GetTotal() );      // base_sales_price
    fields.FormatPush( "%u", this->ammo_item_type_id );     // item_type_id_ammo
    fields.FormatPush( "%u", this->anim_list->Get(0)->id ); // item_anim_id
    fields.Push( this->description );                       // description
    fields.FormatPush ("%1.2f",this->weaponStats.Latency()); // weapon_speed
    fields.FormatPush ("%1.2f",this->weaponStats.TargetedBlockValue()); //weapon_block_targeted
    fields.FormatPush ("%1.2f",this->weaponStats.UntargetedBlockValue()); //weapon_block_untargeted
    fields.FormatPush ("%1.2f",this->weaponStats.CounterBlockValue()); //weapon_counterblock

    fields.FormatPush("%1.2f", this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_SLASH] );            
    fields.FormatPush("%1.2f", this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_BLUNT] );            
    fields.FormatPush("%1.2f", this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_PIERCE] );            
    fields.FormatPush("%1.2f", this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_FORCE] );            
    fields.FormatPush("%1.2f", this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_FIRE] );            
    fields.FormatPush("%1.2f", this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_ICE] );            
    fields.FormatPush("%1.2f", this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_AIR] );            
    fields.FormatPush("%1.2f", this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_POISON] );            
    fields.FormatPush("%1.2f", this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_DISEASE] );            
    fields.FormatPush("%1.2f", this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_HOLY] );            
     fields.FormatPush("%1.2f", this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_UNHOLY] );            

    fields.FormatPush("%1.2f", this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_SLASH] );            
    fields.FormatPush("%1.2f", this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_BLUNT] );            
    fields.FormatPush("%1.2f", this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_PIERCE] );            
    fields.FormatPush("%1.2f", this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_FORCE] );            
    fields.FormatPush("%1.2f", this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_FIRE] );            
    fields.FormatPush("%1.2f", this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_ICE] );            
    fields.FormatPush("%1.2f", this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_AIR] );            
    fields.FormatPush("%1.2f", this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_POISON] );            
    fields.FormatPush("%1.2f", this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_DISEASE] );            
    fields.FormatPush("%1.2f", this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_HOLY] );            
     fields.FormatPush("%1.2f", this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_UNHOLY] );       

    fields.FormatPush("%u", CacheManager::GetSingleton().FindCommonStringID( this->mesh_name ) );    //Mesh   

    // stat requirements
    fields.Push( this->reqs[0].name);
    fields.FormatPush( "%1.2f", this->reqs[0].min_value);            
    fields.Push( this->reqs[1].name);
    fields.FormatPush( "%1.2f", this->reqs[1].min_value);            
    fields.Push( this->reqs[2].name);
    fields.FormatPush( "%1.2f", this->reqs[2].min_value);

    // equip/unequip events
    fields.Push( this->progressionEventEquip );
    fields.Push( this->progressionEventUnEquip );
     
    // Save this entry
    update_ok &= db->GenericUpdateWithID("item_stats", "id", GetUIDStr(), fieldnames, fields );

    return update_ok;
}

// If the item is a weapon, use a whole string
// If the item is an armor, first char is 1,2,3 and second is a,b,c
void psItemStats::SetArmorVsWeaponType(const char* v)
{
    if(!v)
        return;

    if (GetIsMeleeWeapon() || GetIsRangeWeapon())
    {
        weapon_type = v;
    }
}

void psItemStats::GetArmorVsWeaponType(csString& buff)
{
    if (GetIsMeleeWeapon() || GetIsRangeWeapon())
    {
        buff = weapon_type;
        return;
    }
    else
    {
        // Need to do some formating here since the values are splitten
        csString formated;
        switch(armorStats.Type())
        {
            case PSITEMSTATS_ARMORTYPE_LIGHT:
            {
                formated = "1";
                break;
            }
            case PSITEMSTATS_ARMORTYPE_MEDIUM:
            {
                formated = "2";
                break;
            }
            case PSITEMSTATS_ARMORTYPE_HEAVY:
            {
                formated = "3";
                break;
            }
            default:
                break;
        }

        formated += armorStats.Class();
        buff = formated;
        return;
    }

    //buff = NULL;
    // @@@ Jorrit: I think the below is the intention rather then the above?
    // Fixes a warning.
    buff.Clear ();
}

int psItemStats::GetAttackAnimID(unsigned int skill_level, iCelEntity *entity)
{
    if (anim_list)
    {
        int max;
        for (max = 0; max < (int)anim_list->Length(); max++)
        {
            if (anim_list->Get(max)->min_level_required >(int) skill_level)
                break;
        }
        int which = psserver->rng->Get(max);
        if (anim_list->Get(which)->anim_id == csInvalidStringID)
        {
            anim_list->Get(which)->anim_id = CacheManager::GetSingleton().GetMsgStrings()->Request(anim_list->Get(which)->anim_name);
            return anim_list->Get(which)->anim_id;
        }
        else
        {
            Debug2(LOG_COMBAT, uid, "Sending anim %s.\n",anim_list->Get(which)->anim_name.GetData() );
            return anim_list->Get(which)->anim_id;
        }
    }
    else
        return 0;
}

bool psItemStats::GetIsConsumable()
{
    return (flags & PSITEMSTATS_FLAG_CONSUMABLE) ? true : false;
}

bool psItemStats::GetIsMeleeWeapon()
{
    return (flags & PSITEMSTATS_FLAG_IS_A_MELEE_WEAPON);
}

bool psItemStats::GetIsArmor()
{
    return (flags & PSITEMSTATS_FLAG_IS_ARMOR) ? true : false;
}

bool psItemStats::IsMoney()
{
    return ( (flags & PSITEMSTATS_FLAG_TRIA) ||
             (flags & PSITEMSTATS_FLAG_HEXA) ||
             (flags & PSITEMSTATS_FLAG_OCTA) ||
             (flags & PSITEMSTATS_FLAG_CIRCLE) );
}

bool psItemStats::GetIsRangeWeapon()
{
    return (flags & PSITEMSTATS_FLAG_IS_A_RANGED_WEAPON) ? true : false;
}


bool psItemStats::GetIsAmmo()
{
    return (flags & PSITEMSTATS_FLAG_IS_AMMO) ? true : false;
}


bool psItemStats::GetIsShield()
{
    return (flags & PSITEMSTATS_FLAG_IS_A_SHIELD)? true : false ;
}


bool psItemStats::GetIsContainer()
{
    return (flags & PSITEMSTATS_FLAG_IS_A_CONTAINER) ? true : false;
}


bool psItemStats::GetCanTransform()
{
    return (flags & PSITEMSTATS_FLAG_CAN_TRANSFORM) ? true : false;
}


bool psItemStats::GetUsesAmmo()
{
    return (flags & PSITEMSTATS_FLAG_USES_AMMO) ? true : false;
}


bool psItemStats::GetIsStackable()
{
    return (flags & PSITEMSTATS_FLAG_IS_STACKABLE) ? true : false;
}


bool psItemStats::GetIsReadable()
{
    return (flags & PSITEMSTATS_FLAG_IS_READABLE) ? true : false;
}


bool psItemStats::GetIsGlyph()
{
    return (flags & PSITEMSTATS_FLAG_IS_GLYPH) ? true : false;
}



uint32 psItemStats::GetUID()
{
    return uid;
}

const char *psItemStats::GetUIDStr()
{
    static char str[20];
    sprintf(str,"%u",uid);
    return str;
}

const char *psItemStats::GetName()
{
    return name;
}

void psItemStats::SetName(const char *v)
{
    if (!v)
        return;

    name = v;
}

const char *psItemStats::GetDescription()
{
    return description;
}

void psItemStats::SetDescription(const char *v)
{
    if (!v)
        return;

    description = v;
}


float psItemStats::GetWeight()
{
    return weight;
}


unsigned short psItemStats::GetSize()
{
    return size;
}


unsigned short psItemStats::GetContainerMaxSize()
{
    return container_max_size;
}

float psItemStats::GetVisibleDistance()
{
    return visible_distance;
}

PSITEMSTATS_SLOTLIST psItemStats::GetValidSlots()
{
    return valid_slots;
}

bool psItemStats::FitsInSlots(PSITEMSTATS_SLOTLIST slotmask)
{
    if ((valid_slots & slotmask)==slotmask)
        return true;
    return false;
}

float psItemStats::GetDecayRate()
{
    return decay_rate;
}


const char *psItemStats::GetMeshName()
{
    return mesh_name;
}
void psItemStats::SetMeshName(const char *v)
{
    mesh_name=v;
}
const char *psItemStats::GetTextureName()
{
    return texture_name;
}
void psItemStats::SetTextureName(const char *v)
{
    texture_name=v;
}
const char *psItemStats::GetPartName()
{
    return texturepart_name;
}
void psItemStats::SetPartName(const char *v)
{
    texturepart_name=v;
}
const char *psItemStats::GetPartMeshName()
{
    return partmesh_name;
}
void psItemStats::SetPartMeshName(const char *v)
{
    partmesh_name=v;
}

const char *psItemStats::GetImageName()
{
    return image_name;
}
void psItemStats::SetImageName(const char *v)
{
    image_name=v;
}

void psItemStats::SetPrice(int trias)
{
    price = psMoney(trias);
}

psMoney& psItemStats::GetPrice()
{
    return price;
}

void psItemStats::SetCategory(psItemCategory * item_category)
{
    category = item_category;
}


psItemCategory * psItemStats::GetCategory()
{
    return category;
}

const char *psItemStats::GetSound()
{
    return sound;
}

void psItemStats::SetSound(const char *v)
{
    sound = csString("item.") + v;
}


bool psItemStats::CheckRequirements( psCharacter* charData, csString& resp )
{
    float val = 0;
    csString needed = "You need to have ";
    bool first= true;

    for ( int z = 0; z < 3; z++ )
    {
        PSITEMSTATS_STAT stat = CacheManager::GetSingleton().ConvertAttributeString(reqs[z].name);
        if ( stat != PSITEMSTATS_STAT_NONE )
        {
            // Stat buffs may be negative; don't use those here
            bool usebuff = charData->GetAttributes()->GetBuffVal(stat) > 0;
            val = charData->GetAttributes()->GetStat(stat,usebuff);
            // TODO: This should just use the buff always when a move from equipment to bulk can't fail
        }
        else
        {
            PSSKILL skill = CacheManager::GetSingleton().ConvertSkillString(reqs[z].name);
            if ( skill != PSSKILL_NONE )
            {
               val = charData->GetSkills()->GetSkillRank( skill );
            }            
        }
        
        if ( val < reqs[z].min_value )
        {
            if(!first)
                needed +=" and ";
            else
                first = false;

            if(reqs[z].min_value - val > 40)
                needed += "a lot ";

            needed += "more in ";
            needed += reqs[z].name;                              
        }
    }   
    
    if(first) // No needed things
    {
        resp = "None";
        return true;         
    }

    needed.Append(" to equip this item");
    resp = needed;
    return false;
}

bool psItemStats::SetRequirement( csString* statName, float statValue )
{

    // The Stat already exists
  for ( int z = 0; z < 3; z++ )
  {
    if ( reqs[z].name.IsEmpty() ) continue;

    if ( statName->CompareNoCase( reqs[z].name ) && reqs[z].min_value < statValue )
    {
        reqs[z].min_value = statValue;
        return true;
    }
  }

    // The Stat didn't exist
  for ( int z = 0; z < 3; z++ )
  {
    if ( reqs[z].name.IsEmpty() )
    {
        reqs[z].name = statName->GetData();
        reqs[z].min_value = statValue;
        return true;
    }
  }

    // No Space Available
    return false;
}

psItem *psItemStats::InstantiateBasicItem(bool transient)
{
    psItem *newitem;

    if (this->GetIsGlyph())
    {
        newitem = new psGlyph();
    }
    else
    {
        newitem = new psItem();
    }

    newitem->SetBaseStats(this);
    newitem->SetCurrentStats(this);

    if (transient)
        newitem->ScheduleRemoval();  // Remove from world in a few hrs if no one picks it up
    newitem->SetCreator(ITEM_BASIC);
    return newitem;  // MUST do newitem->Loaded() when it is safe to save!
}

bool psItemStats::SetAttribute( csString* op, csString* attrName, float modifier)
{
    csString Operation = op->GetData(),
  AttributeName = attrName->GetData();

    float* value[5] = { NULL, NULL, NULL, NULL, NULL };
     // Attribute Names:
    // item
    //        weight
    //        damage
    //            slash
    //            pierce
    //            blunt
    //            ...
    //        protection
    //            slash
    //            pierce
    //            blunt
    //            ...
    //        speed

    // target
    // wielder

  AttributeName.Downcase();
    if ( AttributeName.Compare( "item.weight" ) )
    {
        value[0] = &this->weight;
    } 
    else if ( AttributeName.Compare( "item.damage_value" ) )
    {
        value[0] = 0;
    }
    else if ( AttributeName.Compare( "item.attack_value" ) )
    {
        value[0] = 0;
    }
    else if ( AttributeName.Compare( "item.speed" ) )
    {
        value[0] = &this->weaponStats.latency;
    }
    else if ( AttributeName.Compare( "item.damage" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_SLASH];
        value[1] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_BLUNT];
        value[2] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_PIERCE];
    }
    else if ( AttributeName.Compare( "item.damage.slash" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_SLASH];
    }
    else if ( AttributeName.Compare( "item.damage.pierce" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_PIERCE];
    }
    else if ( AttributeName.Compare( "item.damage.blunt" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_BLUNT];
    }
    else if ( AttributeName.Compare( "item.damage.force" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_FORCE];
    }
    else if ( AttributeName.Compare( "item.damage.fire" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_FIRE];
    }
    else if ( AttributeName.Compare( "item.damage.ice" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_ICE];
    }
    else if ( AttributeName.Compare( "item.damage.air" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_AIR];
    }
    else if ( AttributeName.Compare( "item.damage.poison" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_POISON];
    }
    else if ( AttributeName.Compare( "item.damage.disease" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_DISEASE];
    }
    else if ( AttributeName.Compare( "item.damage.holy" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_HOLY];
    }
    else if ( AttributeName.Compare( "item.damage.unholy" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_UNHOLY];
    }

    else if ( AttributeName.Compare( "item.protection" ) )
    {
        value[0] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_SLASH];
        value[1] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_BLUNT];
        value[2] = &this->weaponStats.damages[PSITEMSTATS_DAMAGETYPE_PIERCE];
    }
    else if ( AttributeName.Compare( "item.protection.slash" ) )
    {
        value[0] = &this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_SLASH];
    }
    else if ( AttributeName.Compare( "item.protection.pierce" ) )
    {
        value[0] = &this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_PIERCE];
    }
    else if ( AttributeName.Compare( "item.protection.blunt" ) )
    {
        value[0] = &this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_BLUNT];
    }
    else if ( AttributeName.Compare( "item.protection.force" ) )
    {
        value[0] = &this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_FORCE];
    }
    else if ( AttributeName.Compare( "item.protection.fire" ) )
    {
        value[0] = &this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_FIRE];
    }
    else if ( AttributeName.Compare( "item.protection.ice" ) )
    {
        value[0] = &this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_ICE];
    }
    else if ( AttributeName.Compare( "item.protection.air" ) )
    {
        value[0] = &this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_AIR];
    }
    else if ( AttributeName.Compare( "item.protection.poison" ) )
    {
        value[0] = &this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_POISON];
    }
    else if ( AttributeName.Compare( "item.protection.disease" ) )
    {
        value[0] = &this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_DISEASE];
    }
    else if ( AttributeName.Compare( "item.protection.holy" ) )
    {
        value[0] = &this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_HOLY];
    }
    else if ( AttributeName.Compare( "item.protection.unholy" ) )
    {
        value[0] = &this->armorStats.damage_protection[PSITEMSTATS_DAMAGETYPE_UNHOLY];
    }

    // Operations  = ADD, MUL, SET
    for ( int i = 0; i < 5; i++ )
    {
        if ( Operation.CompareNoCase( "ADD" ) )
        {
            if (value[i]) *value[i] += modifier;
        }

        if ( Operation.CompareNoCase( "MUL" ) )
        {
            if (value[i]) *value[i] *= modifier;
        }

        if ( Operation.CompareNoCase( "VAL" ) )
        {
            if (value[i]) *value[i] = modifier;
        }
    }

    return true;
}

