mirror of
https://gitlab.com/hashborgir/d2tools.git
synced 2024-11-30 12:36:03 +00:00
go back to old d2char/d2item. will work on new one later
This commit is contained in:
parent
caf675cff5
commit
9bc9868b29
@ -155,7 +155,7 @@ WHERE sk.charclass = '$class' AND sk.skilldesc != 'dummy'";
|
||||
// readh(offset, numbytes), convert toBits, feed into D2Item
|
||||
$_items = [];
|
||||
foreach ($itemOffsets as $offset => $bytes) {
|
||||
//$this->items[] = new D2Item($this->ByteReader->toBits($this->ByteReader->readh($offset, $bytes)), $this->cData['CharacterName']); // Create a new D2Item object and add it to the items array
|
||||
$this->items[] = new D2Item($this->ByteReader->toBits($this->ByteReader->readh($offset, $bytes))); // Create a new D2Item object and add it to the items array
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,18 +239,15 @@ WHERE sk.charclass = '$class' AND sk.skilldesc != 'dummy'";
|
||||
$cData['NPCIntroductions'] = $this->bData[714];
|
||||
$cData['filePath'] = $this->filePath;
|
||||
|
||||
|
||||
// returns an array of items,
|
||||
// each item is an array of item details
|
||||
$this->parseItems(); // parse items will populate $this->items
|
||||
$cData['items'] = $this->items; // cData[items] will be $this->items
|
||||
$this->cData = $cData;
|
||||
// parse stats
|
||||
$this->parseStats();
|
||||
$this->cData['skills'] = $this->parseSkills();
|
||||
|
||||
// returns an array of items,
|
||||
// each item is an array of item details
|
||||
$this->parseItems(); // parse items will populate $this->items
|
||||
$cData['items'] = $this->items; // cData[items] will be $this->items
|
||||
|
||||
|
||||
unset($this->items);
|
||||
//unset($this->bData);
|
||||
unset($this->sData);
|
||||
|
345
src/D2Item.php
345
src/D2Item.php
@ -6,10 +6,353 @@ require_once 'D2Strings.php';
|
||||
require_once 'D2Functions.php';
|
||||
require_once 'D2ItemData.php';
|
||||
|
||||
|
||||
class D2Item {
|
||||
|
||||
/**
|
||||
* @var null
|
||||
*/
|
||||
private $bits = null;
|
||||
|
||||
/**
|
||||
* @var null
|
||||
*/
|
||||
public $iData = null;
|
||||
|
||||
/**
|
||||
* @param $bits
|
||||
*/
|
||||
public function __construct($bits) {
|
||||
if ($bits == '')
|
||||
return false;
|
||||
$this->bits = $bits;
|
||||
return $this->parseItem();
|
||||
}
|
||||
|
||||
/* set $this->iData to array of item details
|
||||
*
|
||||
* @return array of item details
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return null
|
||||
*/
|
||||
private function parseItem() {
|
||||
$b = new D2BitReader($this->bits);
|
||||
$b->skip(16); // Skip JM
|
||||
$b->skip(4); // skip unknown 4 bytes
|
||||
$this->iData['identified'] = $b->read(1); // bit 20, identified
|
||||
$b->skip(6); // skip unknown 6 bytes
|
||||
$this->iData['socketed'] = $b->read(1); // bit 27, socketed
|
||||
$b->skip(1);
|
||||
// This bit is set on items which you have picked up since the last time the game was saved.
|
||||
$this->iData['pickedUpSinceLastSave'] = $b->read(1); // bit 29
|
||||
$b->skip(2);
|
||||
$this->iData['ear'] = $b->read(1); // bit 32 bool
|
||||
$this->iData['startingItem'] = $b->read(1); // bit 33 bool
|
||||
$b->skip(3);
|
||||
$this->iData['compact'] = $b->read(1); // bit 37 compact
|
||||
$this->iData['ethereal'] = $b->read(1); // bit 38 ethereal
|
||||
$b->skip(1); // unknown, seems always 1
|
||||
$this->iData['personalized'] = $b->read(1); // bit 40 Item has been personalized (by Anya in Act V)
|
||||
$b->skip(1);
|
||||
$this->iData['runeword'] = $b->read(1); // bit 42 the item has been given a Rune Word.
|
||||
$b->skip(15); // unknown; some of these bits may be set
|
||||
// item location
|
||||
$location = bindec($b->readr(3)); // bit 58 parent Item location.
|
||||
$body = bindec($b->readr(4)); // bit 61 If the item is equipped
|
||||
$col = bindec($b->readr(4)); // bit 65 Column number of the left corner of the item
|
||||
$row = bindec($b->readr(4)); // bit 69 Row number of the top of the item, counting from 0.
|
||||
$_stored = bindec($b->readr(3)); // bit 73
|
||||
// The number of gems (or skulls or jewels) which have been glued to this item (if socketed). There will be this many additional item structures for the gems immediately following this item, in the order that the gems were inserted.
|
||||
$b->seek(108);
|
||||
$this->iData['gems_in'] = bindec($b->readr(3));
|
||||
|
||||
// bit 111, 32bits, Unique identifier. Diablo II randomly generates a value for this field in order to discourage cheaters from "duping" items.
|
||||
//This appears to be the item's level; i.e., the level with which the item was created (or 'dropped'). The item level is based on the level of the monster who dropped it, the level of the area you're in if found in a chest, or, in rare cases, your characters level. The item level determines what modifiers are allowed on the item.
|
||||
$b->seek(143);
|
||||
$this->iData['ilvl'] = bindec($b->readr(7));
|
||||
|
||||
$b->seek(150);
|
||||
$quality = bindec($b->readr(4));
|
||||
|
||||
switch ($quality) {
|
||||
case D2ItemQuality::LOW_QUALITY:
|
||||
$this->iData['iquality'] = "Low Quality";
|
||||
break;
|
||||
case D2ItemQuality::NORMAL:
|
||||
$this->iData['iquality'] = "Normal";
|
||||
break;
|
||||
case D2ItemQuality::HIGH_QUALITY:
|
||||
$this->iData['iquality'] = "High Quality";
|
||||
break;
|
||||
case D2ItemQuality::MAGIC:
|
||||
$this->iData['iquality'] = "Magic";
|
||||
break;
|
||||
case D2ItemQuality::SET:
|
||||
$this->iData['iquality'] = "Set";
|
||||
break;
|
||||
case D2ItemQuality::RARE:
|
||||
$this->iData['iquality'] = "Rare";
|
||||
break;
|
||||
case D2ItemQuality::UNIQUE:
|
||||
$this->iData['iquality'] = "Unique";
|
||||
break;
|
||||
case D2ItemQuality::CRAFTED:
|
||||
$this->iData['iquality'] = "Crafted";
|
||||
break;
|
||||
default:
|
||||
$this->iData['iquality'] = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
// ring/amu/jew/char or not. 1 or 0. if 1, next 3 are set
|
||||
// If this bit is set, the item has one of multiple pictures associated with it; the next field determines which picture a particular item uses. If this bit is 0, the next field is absent. The picture field is used for rings, amulets, jewels, and charms.
|
||||
// After the above data, if the item is a ring, amulet, jewel, or charm, then it has a 1 bit followed by three more bits. All other items (that I've seen) have just a single 0 bit.
|
||||
$b->seek(151); // ring bit
|
||||
$ring = bindec($b->read(1));
|
||||
if ($ring) { // we read 1 bit at 151, end up at 152
|
||||
$b->seek(152);
|
||||
$ringPic = bindec($b->readr(3)); // for jew, amu, rin, char
|
||||
}
|
||||
// if ring bit is 0, go to 152 and read 1 bit
|
||||
else {
|
||||
// This bit apparently is set for certain class-specific Expansion Set items. It indicates the presence of the next 11-bit field. If this bit is 0, the next field is absent.
|
||||
$b->seek(152);
|
||||
$_class_specific = bindec($b->read(1));
|
||||
}
|
||||
/* Credit for the discovery of this field's meaning goes entirely to Guillaume Courtin of France. Thanks! :->
|
||||
This field indicates magic properties which are inherent in certain class-specific items. A given class-specific item will (almost) always start with the same set of properties, even if its quality is "normal". Other quality ratings may add more properties to the standard set. It appears that items which will have this field are:
|
||||
Amazon-only bows, spears, and javelins
|
||||
Voodoo heads (Necromancer-only shields)
|
||||
Paladin-only shields
|
||||
Orbs (Sorceress-Only wands)
|
||||
*/
|
||||
// if class specific bit (152) is 1, then go to 153 and read 11 bits
|
||||
if ($_class_specific) {
|
||||
$b->seek(153);
|
||||
$class_specific = bindec($b->readr(11));
|
||||
}
|
||||
|
||||
|
||||
//var_dump($b->getOffset());
|
||||
|
||||
//
|
||||
{
|
||||
// if 151 is 1, but 152 is 0, then we have read 152/153/154 already, so we should be at 155
|
||||
if ($ring && !$_class_specific) {
|
||||
$b->seek(155);
|
||||
// if 151 is 1 then we're at 155. if 152 is 1, then we read from 155 to 165, arrive at bit 166
|
||||
} else if ($ring && $_class_specific) {
|
||||
$b->seek(166);
|
||||
// if 151 is 0, we alreay just read it, then we're at 152. if 152 is 1, then we read from 153 to 163, arrive at 164
|
||||
} else if ($_class_specific && !$ring) {
|
||||
$b->seek(164);
|
||||
}
|
||||
|
||||
switch ($quality) {
|
||||
case D2ItemQuality::LOW_QUALITY:
|
||||
$low_quality_item_data = bindec($b->readr(3));
|
||||
switch ($low_quality_item_data) {
|
||||
case 0:
|
||||
$this->iData['low_quality_item_data'] = "Crude";
|
||||
break;
|
||||
case 1:
|
||||
$this->iData['low_quality_item_data'] = "Cracked";
|
||||
break;
|
||||
case 2:
|
||||
$this->iData['low_quality_item_data'] = "Damaged";
|
||||
break;
|
||||
case 3:
|
||||
$this->iData['low_quality_item_data'] = "Low Quality";
|
||||
break;
|
||||
default:
|
||||
$this->iData['low_quality_item_data'] = "Unknown quality";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// Normal items have no extra quality data.
|
||||
case D2ItemQuality::NORMAL:
|
||||
break;
|
||||
case D2ItemQuality::HIGH_QUALITY:
|
||||
$high_quality_item_data = bindec($b->readr(3));
|
||||
break;
|
||||
case D2ItemQuality::MAGIC:
|
||||
// read 11 bits, prefix.
|
||||
// if no prefix, then next 11 bits will be suffix
|
||||
$this->iData['magic_prefix'] = bindec($b->readr(11));
|
||||
$this->iData['magic_suffix'] = bindec($b->readr(11));
|
||||
break;
|
||||
case D2ItemQuality::SET:
|
||||
// Set items have a 12-bit field containing the ID of the set. (Not the set member, but the whole set.) The set member is identified by cross-referencing the item type with the set ID. Also note that set items have an extra field following the item-specific data.
|
||||
// Set identifier; i.e., all items which are part of the set will have the same value in this field.
|
||||
//var_dump($b->getOffset());
|
||||
$setid = bindec($b->readr(12));
|
||||
//var_dump($setid);
|
||||
$sql = "SELECT ROWID,* from sets WHERE ROWID=$setid";
|
||||
$res = PDO_FetchRow($sql);
|
||||
|
||||
//ddump($res);
|
||||
|
||||
$this->iData["setname"] = $res;
|
||||
|
||||
break;
|
||||
case D2ItemQuality::RARE:
|
||||
$this->iData['iquality'] = "Rare";
|
||||
break;
|
||||
case D2ItemQuality::UNIQUE:
|
||||
//Unique items have an additional 12 bit field, which in most cases is the unique item ID. The few exceptions are certain quest items (e.g., the Horadric Malus).
|
||||
$uni_item = bindec($b->readr(12));
|
||||
break;
|
||||
case D2ItemQuality::CRAFTED:
|
||||
$this->iData['iquality'] = "Crafted";
|
||||
break;
|
||||
default:
|
||||
$this->iData['iquality'] = "Unknown";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// weird behavior/bug
|
||||
// if item is in a container, bodypart will be NULL
|
||||
// if item is on bodypart, container will be NULL
|
||||
switch ($location) {
|
||||
case D2ItemLocation::BELT:
|
||||
$this->iData['container'] = 'Belt';
|
||||
break;
|
||||
case D2ItemLocation::CURSOR:
|
||||
break;
|
||||
case D2ItemLocation::SOCKET:
|
||||
break;
|
||||
case D2ItemLocation::STORED:
|
||||
switch ($_stored) {
|
||||
case D2ItemLocationStored::NONE:
|
||||
|
||||
$this->iData['container'] = 'None'; // item is not stored, check bit 58
|
||||
break;
|
||||
case D2ItemLocationStored::INVENTORY:
|
||||
$this->iData['container'] = 'Inventory';
|
||||
break;
|
||||
case D2ItemLocationStored::CUBE:
|
||||
$this->iData['container'] = 'Horadric Cube';
|
||||
break;
|
||||
case D2ItemLocationStored::STASH:
|
||||
$this->iData['container'] = 'Stash';
|
||||
break;
|
||||
default: $this->iData['container'] = 'Unknown';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case D2ItemLocation::EQUIPPED:
|
||||
switch ($body) {
|
||||
case D2ItemLocationBody::HELMET: $this->iData['location'] = 'Helmet';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
case D2ItemLocationBody::AMULET: $this->iData['location'] = 'Amulet';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
case D2ItemLocationBody::ARMOR: $this->iData['location'] = 'Armor';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
case D2ItemLocationBody::WEAPONR: $this->iData['location'] = 'Weapon R';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
case D2ItemLocationBody::WEAPONL: $this->iData['location'] = 'Weapon L';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
case D2ItemLocationBody::RINGR: $this->iData['location'] = 'Ring R';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
case D2ItemLocationBody::RINGL: $this->iData['location'] = 'Ring L';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
case D2ItemLocationBody::BELT: $this->iData['location'] = 'Belt';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
case D2ItemLocationBody::BOOTS: $this->iData['location'] = 'Boots';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
case D2ItemLocationBody::GLOVES: $this->iData['location'] = 'Gloves';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
case D2ItemLocationBody::WEAPONR2: $this->iData['location'] = 'Weapon Alt R';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
case D2ItemLocationBody::WEAPONL2: $this->iData['location'] = 'Weapon Alt L';
|
||||
$this->iData['container'] = 'Body';
|
||||
break;
|
||||
default: $this->iData['location'] = 'Unknown';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// if item is ear
|
||||
if ($this->iData['ear']) {
|
||||
// set item code/basename
|
||||
$this->iData['itemCode'] = 'ear';
|
||||
$this->iData['basename'] = 'Ear';
|
||||
// get ear class/level
|
||||
$eclass = bindec($b->readr(3)); // bit 76
|
||||
$elevel = bindec($b->readr(7)); // bit 79
|
||||
// get ear char's name
|
||||
}
|
||||
|
||||
// get item code
|
||||
$b->seek(76);
|
||||
$itemCode = '';
|
||||
foreach (str_split($b->read(32), 8) as $byte) {
|
||||
$itemCode .= chr(bindec(strrev($byte)));
|
||||
}
|
||||
$this->iData['code'] = trim($itemCode);
|
||||
|
||||
$sql = "SELECT * from armor WHERE code = '{$this->iData['code']}'";
|
||||
$res = PDO_FetchRow($sql);
|
||||
if (empty($res)) {
|
||||
$sql = "SELECT * from misc WHERE code = '{$this->iData['code']}'";
|
||||
$res = PDO_FetchRow($sql);
|
||||
}
|
||||
if (empty($res)) {
|
||||
$sql = "SELECT * from weapons WHERE code = '{$this->iData['code']}'";
|
||||
$res = PDO_FetchRow($sql);
|
||||
}
|
||||
|
||||
// set txt data array
|
||||
$this->iData['txt'] = ($res);
|
||||
|
||||
$sql = "
|
||||
SELECT code, namestr
|
||||
FROM armor
|
||||
WHERE code = '{$this->iData['code']}'
|
||||
UNION
|
||||
SELECT code, namestr
|
||||
FROM misc
|
||||
WHERE code = '{$this->iData['code']}'
|
||||
UNION
|
||||
SELECT code, namestr
|
||||
FROM weapons
|
||||
WHERE code = '{$this->iData['code']}'
|
||||
";
|
||||
$res = PDO_FetchAssoc($sql);
|
||||
|
||||
$sql = "SELECT `String` FROM strings WHERE `Key`='{$res[$this->iData['code']]}'";
|
||||
$res = PDO_FetchOne($sql);
|
||||
$this->iData['basename'] = $res;
|
||||
$this->iData['basename'] = preg_replace('/ÿc[0-9]/', '', $this->iData['basename']);
|
||||
|
||||
return $this->iData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class D2Item {
|
||||
class D2Item_NEW {
|
||||
|
||||
private $bits = null; // Item bitstring
|
||||
private $oldbits = null; // Old Item bits for comparison
|
||||
|
Loading…
Reference in New Issue
Block a user