Blocking

Apr 11, 2010, 1:24 am
#1
Joined: Dec 3, 2007
Occupation: Chaos Weaver
Location: Standing between all life and death
Posts: 2,898
Can anyone tell me how blocking works in IVAN? I think this is it, but I can't make head or tail of it.

SPOILER ALERT. Click here to see text.
/*
 *
 *  Iter Vehemens ad Necem (IVAN)
 *  Copyright (C) Timo Kiviluoto
 *  Released under the GNU General
 *  Public License
 *
 *  See LICENSING which should be included
 *  along with this file for more details
 *
 */

/* Compiled through itemset.cpp */

const char* ToHitValueDescription[] = { "unbelievably inaccurate", "extremely inaccurate", "inaccurate", "decently accurate", "accurate", "highly accurate", "extremely accurate", "unbelievably accurate" };
const char* StrengthValueDescription[] = { "fragile", "rather sturdy", "sturdy", "strong", "very strong", "extremely strong", "almost unbreakable" };

itemprototype::itemprototype(const itemprototype* Base, itemspawner Spawner, itemcloner Cloner, const char* ClassID) : Base(Base), Spawner(Spawner), Cloner(Cloner), ClassID(ClassID) { Index = protocontainer<item>::Add(this); }

truth itemdatabase::AllowRandomInstantiation() const { return !(Config & S_LOCK_ID); }

item::item() : Slot(0), CloneMotherID(0), Fluid(0), LifeExpectancy(0) { }
truth item::IsOnGround() const { return Slot[0]->IsOnGround(); }
truth item::IsSimiliarTo(item* Item) const { return Item->GetType() == GetType() && Item->GetConfig() == GetConfig(); }
double item::GetBaseDamage() const { return sqrt(5e-5 * GetWeaponStrength()) + GetDamageBonus(); }
int item::GetBaseMinDamage() const { return int(GetBaseDamage() * 0.75); }
int item::GetBaseMaxDamage() const { return int(GetBaseDamage() * 1.25) + 1; }
int item::GetBaseToHitValue() const { return int(10000. / (1000 + GetWeight()) + GetTHVBonus()); }
int item::GetBaseBlockValue() const { return int((10000. / (1000 + GetWeight()) + GetTHVBonus()) * GetBlockModifier() / 10000.); }
truth item::IsInCorrectSlot(int I) const { return I == RIGHT_WIELDED_INDEX || I == LEFT_WIELDED_INDEX; }
truth item::IsInCorrectSlot() const { return IsInCorrectSlot(static_cast<gearslot*>(*Slot)->GetEquipmentIndex()); }
int item::GetEquipmentIndex() const { return static_cast<gearslot*>(*Slot)->GetEquipmentIndex(); }
int item::GetGraphicsContainerIndex() const { return GR_ITEM; }
truth item::IsBroken() const { return GetConfig() & BROKEN; }
const char* item::GetBreakVerb() const { return "breaks"; }
square* item::GetSquareUnderEntity(int I) const { return GetSquareUnder(I); }
square* item::GetSquareUnder(int I) const { return Slot[I] ? Slot[I]->GetSquareUnder() : 0; }
lsquare* item::GetLSquareUnder(int I) const { return static_cast<lsquare*>(Slot[I]->GetSquareUnder()); }
void item::SignalStackAdd(stackslot* StackSlot, void (stack::*)(item*, truth)) { Slot[0] = StackSlot; }
truth item::IsAnimated() const { return GraphicData.AnimationFrames > 1 || (Fluid && ShowFluids()); }
truth item::IsRusted() const { return MainMaterial->GetRustLevel() != NOT_RUSTED; }
truth item::IsEatable(const character* Eater) const { return GetConsumeMaterial(Eater, &material::IsSolid) && IsConsumable(); }
truth item::IsDrinkable(const character* Eater) const { return GetConsumeMaterial(Eater, &material::IsLiquid) && IsConsumable(); }
pixelpredicate item::GetFluidPixelAllowedPredicate() const { return &rawbitmap::IsTransparent; }
void item::Cannibalize() { Flags |= CANNIBALIZED; }
void item::SetMainMaterial(material* NewMaterial, int SpecialFlags) { SetMaterial(MainMaterial, NewMaterial, GetDefaultMainVolume(), SpecialFlags); }
void item::ChangeMainMaterial(material* NewMaterial, int SpecialFlags) { ChangeMaterial(MainMaterial, NewMaterial, GetDefaultMainVolume(), SpecialFlags); }
void item::InitMaterials(const materialscript* M, const materialscript*, truth CUP) { InitMaterials(M->Instantiate(), CUP); }
int item::GetMainMaterialRustLevel() const { return MainMaterial->GetRustLevel(); }

item::item(const item& Item) : object(Item), Slot(0), Size(Item.Size), DataBase(Item.DataBase), Volume(Item.Volume), Weight(Item.Weight), Fluid(0), SquaresUnder(Item.SquaresUnder), LifeExpectancy(Item.LifeExpectancy)
{
  Flags &= ENTITY_FLAGS|SQUARE_POSITION_BITS;
  ID = game::CreateNewItemID(this);
  CloneMotherID = new idholder(Item.ID);
  idholder* TI = CloneMotherID;

  for(idholder* II = Item.CloneMotherID; II; II = II->Next)
    TI = TI->Next = new idholder(II->ID);

  TI->Next = 0;
  Slot = new slot*[SquaresUnder];

  for(int c = 0; c < SquaresUnder; ++c)
    Slot[c] = 0;
}

item::~item()
{
  delete [] Slot;
  game::RemoveItemID(ID);

  fluid** FP = Fluid;

  if(FP)
  {
    for(int c = 0; c < SquaresUnder; ++c)
      for(fluid* F = FP[c]; F;)
      {
	fluid* ToDel = F;
	F = F->Next;
	delete ToDel;
      }

    delete [] FP;
  }
}

void item::Fly(character* Thrower, int Direction, int Force)
{
  int Range = Force * 25 / Max(long(sqrt(GetWeight())), 1L);

  lsquare* LSquareUnder = GetLSquareUnder();
  RemoveFromSlot();
  LSquareUnder->GetStack()->AddItem(this, false);

  if(!Range || GetSquaresUnder() != 1)
  {
    if(GetLSquareUnder()->GetRoom())
      GetLSquareUnder()->GetRoom()->AddItemEffect(this);

    return;
  }

  if(Direction == RANDOM_DIR)
    Direction = RAND() & 7;

  v2 StartingPos = GetPos();
  v2 Pos = StartingPos;
  v2 DirVector = game::GetMoveVector(Direction);
  truth Breaks = false;
  double BaseDamage, BaseToHitValue;

  /*** check ***/

  if(Thrower)
  {
    int Bonus = Thrower->IsHumanoid() ? Thrower->GetCWeaponSkill(GetWeaponCategory())->GetBonus() : 1000;
    BaseDamage = sqrt(5e-12 * GetWeaponStrength() * Force / Range) * Bonus;
    BaseToHitValue = 10 * Bonus * Thrower->GetMoveEase() / (500 + GetWeight()) * Thrower->GetAttribute(DEXTERITY) * sqrt(2.5e-8 * Thrower->GetAttribute(PERCEPTION)) / Range;
  }
  else
  {
    BaseDamage = sqrt(5e-6 * GetWeaponStrength() * Force / Range);
    BaseToHitValue = 10 * 100 / (500 + GetWeight()) / Range;
  }

  int RangeLeft;

  for(RangeLeft = Range; RangeLeft; --RangeLeft)
  {
    if(!GetLevel()->IsValidPos(Pos + DirVector))
      break;

    lsquare* JustHit = GetNearLSquare(Pos + DirVector);

    if(!JustHit->IsFlyable())
    {
      Breaks = true;
      JustHit->GetOLTerrain()->HasBeenHitByItem(Thrower, this, int(BaseDamage * sqrt(RangeLeft)));
      break;
    }
    else
    {
      clock_t StartTime = clock();
      Pos += DirVector;
      RemoveFromSlot();
      JustHit->GetStack()->AddItem(this, false);
      truth Draw = game::OnScreen(JustHit->GetPos()) && JustHit->CanBeSeenByPlayer();

      if(Draw)
	game::DrawEverything();

      if(JustHit->GetCharacter())
      {
	int Damage = int(BaseDamage * sqrt(RangeLeft));
	double ToHitValue = BaseToHitValue * RangeLeft;
	int Returned = HitCharacter(Thrower, JustHit->GetCharacter(), Damage, ToHitValue, Direction);

	if(Returned == HIT)
	  Breaks = true;

	if(Returned != MISSED)
	  break;
      }

      if(Draw)
	while(clock() - StartTime < 0.03 * CLOCKS_PER_SEC);
    }
  }

  if(Breaks)
    ReceiveDamage(Thrower, int(sqrt(GetWeight() * RangeLeft) / 10), THROW|PHYSICAL_DAMAGE, Direction);

  if(Exists() && GetLSquareUnder()->GetRoom())
    GetLSquareUnder()->GetRoom()->AddItemEffect(this);
}

int item::HitCharacter(character* Thrower, character* Dude, int Damage, double ToHitValue, int Direction)
{
  if(Dude->Catches(this))
    return CATCHED;

  if(Thrower && !EffectIsGood())
    Thrower->Hostility(Dude);

  if(Dude->DodgesFlyingItem(this, ToHitValue))
  {
    if(Dude->IsPlayer())
      ADD_MESSAGE("%s misses you.", CHAR_NAME(DEFINITE));
    else if(Dude->CanBeSeenByPlayer())
      ADD_MESSAGE("%s misses %s.", CHAR_NAME(DEFINITE), Dude->CHAR_NAME(DEFINITE));

    return MISSED;
  }

  Dude->HasBeenHitByItem(Thrower, this, Damage, ToHitValue, Direction);
  return HIT;
}

double item::GetWeaponStrength() const
{
  return GetFormModifier() * GetMainMaterial()->GetStrengthValue() * sqrt(GetMainMaterial()->GetWeight());
}

int item::GetStrengthRequirement() const
{
  double WeightTimesSize = GetWeight() * GetSize();
  return int(1.25e-10 * WeightTimesSize * WeightTimesSize);
}

truth item::Apply(character* Applier)
{
  if(Applier->IsPlayer())
    ADD_MESSAGE("You can't apply this!");

  return false;
}

/* Returns truth that tells whether the Polymorph really happened */

truth item::Polymorph(character* Polymorpher, stack* CurrentStack)
{
  if(!IsPolymorphable())
    return false;
  else
  {
    if(Polymorpher && IsOnGround())
    {
      room* Room = GetRoom();

      if(Room)
	Room->HostileAction(Polymorpher);
    }

    if(GetSquarePosition() != CENTER)
    {
      stack* Stack = CurrentStack->GetLSquareUnder()->GetStackOfAdjacentSquare(GetSquarePosition());

      if(Stack)
	CurrentStack = Stack;
    }

    CurrentStack->AddItem(protosystem::BalancedCreateItem(0, MAX_PRICE, ANY_CATEGORY, 0, 0, 0, true));
    RemoveFromSlot();
    SendToHell();
    return true;
  }
}

/* Returns whether the Eater must stop eating the item */

truth item::Consume(character* Eater, long Amount)
{
  material* ConsumeMaterial = GetConsumeMaterial(Eater);

  if(!ConsumeMaterial)
    return true;

  if(Eater->IsPlayer() && !(Flags & CANNIBALIZED) && Eater->CheckCannibalism(ConsumeMaterial))
  {
    game::DoEvilDeed(25);
    ADD_MESSAGE("You feel that this was an evil deed.");
    Cannibalize();
  }

  ulong ID = GetID();
  material* Garbage = ConsumeMaterial->EatEffect(Eater, Amount);
  item* NewConsuming = GetID() ? this : game::SearchItem(ID);
  material* NewConsumeMaterial = NewConsuming->GetConsumeMaterial(Eater);

  if(!NewConsuming->Exists()
     || !NewConsumeMaterial
     || !NewConsumeMaterial->IsSameAs(ConsumeMaterial))
    ConsumeMaterial->FinishConsuming(Eater);

  delete Garbage;
  return !NewConsuming->Exists() || !NewConsumeMaterial;
}

truth item::CanBeEatenByAI(const character* Eater) const
{
  material* ConsumeMaterial = GetConsumeMaterial(Eater);

  return (!Eater->IsPet()
	  || !(Eater->GetCommandFlags() & DONT_CONSUME_ANYTHING_VALUABLE)
	  || !IsValuable())
    && IsConsumable()
    && ConsumeMaterial && ConsumeMaterial->CanBeEatenByAI(Eater);
}

void item::Save(outputfile& SaveFile) const
{
  SaveFile << (ushort)GetType();
  object::Save(SaveFile);
  SaveFile << (ushort)GetConfig();
  SaveFile << (ushort)Flags;
  SaveFile << Size << ID << LifeExpectancy;
  SaveLinkedList(SaveFile, CloneMotherID);

  if(Fluid)
  {
    SaveFile.Put(true);

    for(int c = 0; c < SquaresUnder; ++c)
      SaveLinkedList(SaveFile, Fluid[c]);
  }
  else
    SaveFile.Put(false);
}

void item::Load(inputfile& SaveFile)
{
  object::Load(SaveFile);
  databasecreator<item>::InstallDataBase(this, ReadType<ushort>(SaveFile));
  Flags |= ReadType<ushort>(SaveFile) & ~ENTITY_FLAGS;
  SaveFile >> Size >> ID >> LifeExpectancy;
  LoadLinkedList(SaveFile, CloneMotherID);

  if(LifeExpectancy)
    Enable();

  game::AddItemID(this, ID);

  if(SaveFile.Get())
  {
    Fluid = new fluid*[SquaresUnder];

    for(int c = 0; c < SquaresUnder; ++c)
    {
      LoadLinkedList(SaveFile, Fluid[c]);

      for(fluid* F = Fluid[c]; F; F = F->Next)
	F->SetMotherItem(this);
    }
  }
}

void item::TeleportRandomly()
{
  if(GetSquaresUnder() == 1) // gum solution
  {
    lsquare* Square = GetNearLSquare(GetLevel()->GetRandomSquare());
    MoveTo(Square->GetStack());

    if(Square->CanBeSeenByPlayer())
      ADD_MESSAGE("Suddenly %s appears!", CHAR_NAME(INDEFINITE));
  }
}

int item::GetStrengthValue() const
{
  return long(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000;
}

void item::RemoveFromSlot()
{
  for(int c = 0; c < SquaresUnder; ++c)
    if(Slot[c])
    {
      try
      {
	Slot[c]->Empty();
      }
      catch(quitrequest)
      {
	SendToHell();
	throw;
      }

      Slot[c] = 0;
    }
}

void item::MoveTo(stack* Stack)
{
  RemoveFromSlot();
  Stack->AddItem(this);
}

const char* item::GetItemCategoryName(long Category) // convert to array
{
  switch(Category)
  {
   case HELMET: return "Helmets";
   case AMULET: return "Amulets";
   case CLOAK: return "Cloaks";
   case BODY_ARMOR: return "Body armors";
   case WEAPON: return "Weapons";
   case SHIELD: return "Shields";
   case RING: return "Rings";
   case GAUNTLET: return "Gauntlets";
   case BELT: return "Belts";
   case BOOT: return "Boots";
   case FOOD: return "Food";
   case POTION: return "Potions";
   case SCROLL: return "Scrolls";
   case BOOK: return "Books";
   case WAND: return "Wands";
   case TOOL: return "Tools";
   case VALUABLE: return "Valuables";
   case MISC: return "Miscellaneous items";
  }

  return "Warezzz";
}

int item::GetResistance(int Type) const
{
  switch(Type&0xFFF)
  {
   case PHYSICAL_DAMAGE: return GetStrengthValue();
   case SOUND:
   case ENERGY:
   case DRAIN:
    return 0;
   case FIRE: return GetFireResistance();
   case POISON: return GetPoisonResistance();
   case ELECTRICITY: return GetElectricityResistance();
   case ACID: return GetAcidResistance();
  }

  ABORT("Resistance lack detected!");
  return 0;
}

truth item::Open(character* Char)
{
  if(Char->IsPlayer())
    ADD_MESSAGE("You can't open %s.", CHAR_NAME(DEFINITE));

  return false;
}

item* itemprototype::SpawnAndLoad(inputfile& SaveFile) const
{
  item* Item = Spawner(0, LOAD);
  Item->Load(SaveFile);
  Item->CalculateAll();
  return Item;
}

void item::LoadDataBaseStats()
{
  SetSize(GetDefaultSize());
}

void item::Initialize(int NewConfig, int SpecialFlags)
{
  CalculateSquaresUnder();
  Slot = new slot*[SquaresUnder];

  for(int c = 0; c < SquaresUnder; ++c)
    Slot[c] = 0;

  if(!(SpecialFlags & LOAD))
  {
    ID = game::CreateNewItemID(this);
    databasecreator<item>::InstallDataBase(this, NewConfig);
    LoadDataBaseStats();
    RandomizeVisualEffects();
    Flags |= CENTER << SQUARE_POSITION_SHIFT;

    if(!(SpecialFlags & NO_MATERIALS))
      GenerateMaterials();
  }

  if(!(SpecialFlags & LOAD))
    PostConstruct();

  if(!(SpecialFlags & (LOAD|NO_MATERIALS)))
  {
    CalculateAll();

    if(!(SpecialFlags & NO_PIC_UPDATE))
      UpdatePictures();
  }
}

truth item::ShowMaterial() const
{
  if(GetMainMaterialConfig().Size == 1)
    return GetMainMaterial()->GetConfig() != GetMainMaterialConfig()[0];
  else
    return true;
}

long item::GetBlockModifier() const
{
  if(!IsShield(0))
    return GetSize() * GetRoundness() << 1;
  else
    return GetSize() * GetRoundness() << 2;
}

truth item::CanBeSeenByPlayer() const
{
  return CanBeSeenBy(PLAYER);
}

truth item::CanBeSeenBy(const character* Who) const
{
  for(int c = 0; c < SquaresUnder; ++c)
    if(Slot[c] && Slot[c]->CanBeSeenBy(Who))
      return true;

  return Who->IsPlayer() && game::GetSeeWholeMapCheatMode();
}

festring item::GetDescription(int Case) const
{
  if(CanBeSeenByPlayer())
    return GetName(Case);
  else
    return CONST_S("something");
}

void item::SignalVolumeAndWeightChange()
{
  CalculateVolumeAndWeight();

  for(int c = 0; c < SquaresUnder; ++c)
    if(Slot[c])
      Slot[c]->SignalVolumeAndWeightChange();
}

void item::CalculateVolumeAndWeight()
{
  Volume = Weight = 0;

  for(int c = 0; c < GetMaterials(); ++c)
    if(GetMaterial(c))
    {
      Volume += GetMaterial(c)->GetVolume();
      Weight += GetMaterial(c)->GetWeight();
    }
}

void item::SignalEmitationIncrease(col24 EmitationUpdate)
{
  if(game::CompareLights(EmitationUpdate, Emitation) > 0)
  {
    game::CombineLights(Emitation, EmitationUpdate);

    for(int c = 0; c < SquaresUnder; ++c)
      if(Slot[c])
	Slot[c]->SignalEmitationIncrease(EmitationUpdate);
  }
}

void item::SignalEmitationDecrease(col24 EmitationUpdate)
{
  if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation)
  {
    col24 Backup = Emitation;
    CalculateEmitation();

    if(Backup != Emitation)
      for(int c = 0; c < SquaresUnder; ++c)
	if(Slot[c])
	  Slot[c]->SignalEmitationDecrease(EmitationUpdate);
  }
}

void item::CalculateAll()
{
  CalculateVolumeAndWeight();
  CalculateEmitation();
}

/* Temporary and buggy. */

void item::WeaponSkillHit(int Hits)
{
  if(Slot[0] && Slot[0]->IsGearSlot())
    static_cast<arm*>(static_cast<gearslot*>(*Slot)->GetBodyPart())->WieldedSkillHit(Hits);
}

/* Returns 0 if item cannot be cloned */

item* item::Duplicate(ulong Flags)
{
  if(!(Flags & IGNORE_PROHIBITIONS)
     && ((!(Flags & MIRROR_IMAGE) && !CanBeCloned())
	 || (Flags & MIRROR_IMAGE && (!CanBeMirrored()
				      || (MainMaterial
					  && !(MainMaterial->GetCommonFlags() & CAN_BE_MIRRORED))
				      || (GetSecondaryMaterial()
					  && !(GetSecondaryMaterial()->GetCommonFlags() & CAN_BE_MIRRORED))))))
    return 0;

  item* Clone = GetProtoType()->Clone(this);

  if(Flags & MIRROR_IMAGE)
    Clone->SetLifeExpectancy(Flags >> LE_BASE_SHIFT & LE_BASE_RANGE,
			     Flags >> LE_RAND_SHIFT & LE_RAND_RANGE);

  idholder* I = new idholder(ID);
  I->Next = CloneMotherID;
  CloneMotherID = I;
  game::RemoveItemID(ID);
  ID = game::CreateNewItemID(this);
  Clone->UpdatePictures();
  return Clone;
}

void item::AddInventoryEntry(const character*, festring& Entry, int Amount, truth ShowSpecialInfo) const
{
  if(Amount == 1)
    AddName(Entry, INDEFINITE);
  else
  {
    Entry << Amount << ' ';
    AddName(Entry, PLURAL);
  }

  if(ShowSpecialInfo)
    Entry << " [" << GetWeight() * Amount << "g]";
}

const itemdatabase* itemprototype::ChooseBaseForConfig(itemdatabase** TempConfig, int Configs, int ConfigNumber)
{
  if(!(ConfigNumber & BROKEN))
    return *TempConfig;
  else
  {
    ConfigNumber ^= BROKEN;

    for(int c = 0; c < Configs; ++c)
      if(TempConfig[c]->Config == ConfigNumber)
	return TempConfig[c];

    return *TempConfig;
  }
}

truth item::ReceiveDamage(character* Damager, int Damage, int Type, int Dir)
{
  if(CanBeBroken() && !IsBroken() && Type & (PHYSICAL_DAMAGE|SOUND|ENERGY|ACID))
  {
    int StrengthValue = GetStrengthValue();

    if(!StrengthValue)
      StrengthValue = 1;

    if(Damage > StrengthValue << 2 && RAND() & 3 && RAND() % (25 * Damage / StrengthValue) >= 100)
    {
      Break(Damager, Dir);
      return true;
    }
  }

  if(Type & ACID && IsBroken() && IsDestroyable(Damager))
  {
    int StrengthValue = GetStrengthValue();

    if(!StrengthValue)
      StrengthValue = 1;

    if(Damage > StrengthValue << 4 && !(RAND() & 3) && RAND() % (100 * Damage / StrengthValue) >= 100)
    {
      Destroy(Damager, Dir);
      return true;
    }
  }

  return false;
}

void itemdatabase::InitDefaults(const itemprototype* NewProtoType, int NewConfig)
{
  IsAbstract = false;
  ProtoType = NewProtoType;
  Config = NewConfig;

  if(NewConfig & BROKEN)
  {
    if(Adjective.GetSize())
      Adjective.Insert(0, "broken ");
    else
      Adjective = CONST_S("broken");

    DefaultSize >>= 1;
    FormModifier >>= 2;
    StrengthModifier >>= 1;
  }
}

long item::GetNutritionValue() const
{
  long NV = 0;

  for(int c = 0; c < GetMaterials(); ++c)
    if(GetMaterial(c))
      NV += GetMaterial(c)->GetTotalNutritionValue();

  return NV;
}

void item::SignalSpoil(material*)
{
  if(!Exists())
    return;

  if(CanBeSeenByPlayer())
    ADD_MESSAGE("%s spoils completely.", GetExtendedDescription().CStr());

  truth Equipped = PLAYER->Equips(this);
  Disappear();

  if(Equipped)
    game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]"));
}

item* item::DuplicateToStack(stack* CurrentStack, ulong Flags)
{
  item* Duplicated = Duplicate(Flags);

  if(!Duplicated)
    return 0;

  CurrentStack->AddItem(Duplicated);
  return Duplicated;
}

truth item::CanBePiledWith(const item* Item, const character* Viewer) const
{
  return GetType() == Item->GetType()
    && GetConfig() == Item->GetConfig()
		    && (WeightIsIrrelevant() || Weight == Item->Weight)
		    && MainMaterial->IsSameAs(Item->MainMaterial)
 && MainMaterial->GetSpoilLevel() == Item->MainMaterial->GetSpoilLevel()
 && MainMaterial->GetRustLevel() == Item->MainMaterial->GetRustLevel()
 && Viewer->GetCWeaponSkillLevel(this) == Viewer->GetCWeaponSkillLevel(Item)
 && Viewer->GetSWeaponSkillLevel(this) == Viewer->GetSWeaponSkillLevel(Item)
		    && !Fluid && !Item->Fluid
 && !LifeExpectancy == !Item->LifeExpectancy;
}

void item::Break(character* Breaker, int)
{
  if(CanBeSeenByPlayer())
    ADD_MESSAGE("%s %s.", GetExtendedDescription().CStr(), GetBreakVerb());

  if(Breaker && IsOnGround())
  {
    room* Room = GetRoom();

    if(Room)
      Room->HostileAction(Breaker);
  }

  item* Broken = GetProtoType()->Clone(this);
  Broken->SetConfig(GetConfig() | BROKEN);
  Broken->SetSize(Broken->GetSize() >> 1);
  DonateFluidsTo(Broken);
  DonateIDTo(Broken);
  DonateSlotTo(Broken);
  SendToHell();

  if(PLAYER->Equips(Broken))
    game::AskForKeyPress(CONST_S("Equipment broken! [press any key to continue]"));
}

void item::Be()
{
  MainMaterial->Be();

  if(Exists() && LifeExpectancy)
    if(LifeExpectancy == 1)
    {
      if(CanBeSeenByPlayer())
	ADD_MESSAGE("%s disappears.", GetExtendedDescription().CStr());

      truth Equipped = PLAYER->Equips(this);
      Disappear();

      if(Equipped)
	game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]"));
    }
    else
      --LifeExpectancy;
}

int item::GetOfferValue(int Receiver) const
{
  /* Temporary */

  int OfferValue = int(sqrt(GetTruePrice()));

  if(Receiver == GetAttachedGod())
    OfferValue <<= 1;
  else
    OfferValue >>= 1;

  return OfferValue;
}

void item::SignalEnchantmentChange()
{
  for(int c = 0; c < SquaresUnder; ++c)
    if(Slot[c])
      Slot[c]->SignalEnchantmentChange();
}

long item::GetEnchantedPrice(int Enchantment) const
{
  return !PriceIsProportionalToEnchantment() ? item::GetPrice() : Max<int>(item::GetPrice() * Enchantment, 0);
}

item* item::Fix()
{
  item* Fixed = this;

  if(IsBroken())
  {
    Fixed = GetProtoType()->Clone(this);
    Fixed->SetConfig(GetConfig() ^ BROKEN);
    Fixed->SetSize(Fixed->GetSize() << 1);
    DonateFluidsTo(Fixed);
    DonateIDTo(Fixed);
    DonateSlotTo(Fixed);
    SendToHell();
  }

  return Fixed;
}

void item::DonateSlotTo(item* Item)
{
  if(Slot[0])
  {
    Slot[0]->DonateTo(Item);
    Slot[0] = 0;

    for(int c = 1; c < SquaresUnder; ++c)
      if(Slot[c])
      {
	Slot[c]->Empty();
	Slot[c] = 0;
      }
  }
}

int item::GetSpoilLevel() const
{
  return MainMaterial->GetSpoilLevel();
}

void item::SignalSpoilLevelChange(material*)
{
  if(!IsAnimated() && GetSpoilLevel() && Slot[0] && Slot[0]->IsVisible())
    for(int c = 0; c < SquaresUnder; ++c)
      GetSquareUnder(c)->IncStaticAnimatedEntities();

  SignalVolumeAndWeightChange(); // gum
  UpdatePictures();
}

truth item::AllowSpoil() const
{
  if(IsOnGround())
  {
    lsquare* Square = GetLSquareUnder();
    int RoomNumber = Square->GetRoomIndex();
    return !RoomNumber || Square->GetLevel()->GetRoom(RoomNumber)->AllowSpoil(this);
  }
  else
    return true;
}

void item::ResetSpoiling()
{
  for(int c = 0; c < GetMaterials(); ++c)
    if(GetMaterial(c))
      GetMaterial(c)->ResetSpoiling();
}

const char* item::GetBaseToHitValueDescription() const
{
  if(GetBaseToHitValue() < 10)
    return ToHitValueDescription[Min(GetBaseToHitValue(), 6)];
  else
    return ToHitValueDescription[7];
}

const char* item::GetBaseBlockValueDescription() const
{
  if(GetBaseBlockValue() < 20)
    return ToHitValueDescription[Min(GetBaseBlockValue() >> 1, 6)];
  else
    return ToHitValueDescription[7];
}

const char* item::GetStrengthValueDescription() const
{
  int SV = GetStrengthValue();

  if(SV < 3)
    return StrengthValueDescription[0];
  else if(SV < 5)
    return StrengthValueDescription[1];
  else if(SV < 8)
    return StrengthValueDescription[2];
  else if(SV < 11)
    return StrengthValueDescription[3];
  else if(SV < 16)
    return StrengthValueDescription[4];
  else if(SV < 20)
    return StrengthValueDescription[5];
  else
    return StrengthValueDescription[6];
}

void item::SpecialGenerationHandler()
{
  if(HandleInPairs())
    Slot[0]->AddFriendItem(Duplicate());
}

void item::SortAllItems(const sortdata& SortData) const
{
  if(SortData.Sorter == 0 || (this->*SortData.Sorter)(SortData.Character))
    SortData.AllItems.push_back(const_cast<item*>(this));
}

int item::GetAttachedGod() const
{
  return DataBase->AttachedGod ? DataBase->AttachedGod : MainMaterial->GetAttachedGod();
}

long item::GetMaterialPrice() const
{
  return MainMaterial->GetRawPrice();
}

long item::GetTruePrice() const
{
  if(LifeExpectancy)
    return 0;

  long Price = Max(GetPrice(), GetMaterialPrice());

  if(WillSpoil())
    Price = Price * (100 - GetMaxSpoilPercentage()) / 500;

  return Price;
}

#ifdef WIZARD

void item::AddAttackInfo(felist& List) const
{
  festring Entry(40, ' ');
  Entry << int(GetWeight());
  Entry.Resize(50);
  Entry << int(GetSize());
  Entry.Resize(60);
  Entry << int(GetStrengthRequirement());
  Entry.Resize(70);
  Entry << GetBaseMinDamage() << '-' << GetBaseMaxDamage();
  List.AddEntry(Entry, LIGHT_GRAY);
}

void item::AddMiscellaneousInfo(felist& List) const
{
  festring Entry(40, ' ');
  Entry << int(GetTruePrice());
  Entry.Resize(55);
  Entry << GetOfferValue(0);
  Entry.Resize(70);
  Entry << int(GetNutritionValue());
  List.AddEntry(Entry, LIGHT_GRAY);
}

#endif

void item::PreProcessForBone()
{
  if(IsQuestItem())
  {
    RemoveFromSlot();
    SendToHell();
  }
  else
  {
    game::RemoveItemID(ID);
    ID = -ID;
    game::AddItemID(this, ID);
  }
}

void item::PostProcessForBone()
{
  boneidmap::iterator BI = game::GetBoneItemIDMap().find(-ID);
  game::RemoveItemID(ID);

  if(BI == game::GetBoneItemIDMap().end())
  {
    ulong NewID = game::CreateNewItemID(this);
    game::GetBoneItemIDMap().insert(std::pair<ulong, ulong>(-ID, NewID));
    ID = NewID;
  }
  else
  {
    if(game::SearchItem(BI->second))
      int esko = esko = 2;

    ID = BI->second;
    game::AddItemID(this, ID);
  }

  for(idholder* I = CloneMotherID; I; I = I->Next)
  {
    BI = game::GetBoneItemIDMap().find(I->ID);

    if(BI == game::GetBoneItemIDMap().end())
    {
      ulong NewCloneMotherID = game::CreateNewItemID(0);
      game::GetBoneItemIDMap().insert(std::pair<ulong, ulong>(I->ID, NewCloneMotherID));
      I->ID = NewCloneMotherID;
    }
    else
      I->ID = BI->second;
  }
}

void item::SetConfig(int NewConfig, int SpecialFlags)
{
  databasecreator<item>::InstallDataBase(this, NewConfig);
  CalculateAll();

  if(!(SpecialFlags & NO_PIC_UPDATE))
    UpdatePictures();
}

god* item::GetMasterGod() const
{
  return game::GetGod(GetConfig());
}

int itemprototype::CreateSpecialConfigurations(itemdatabase** TempConfig, int Configs)
{
  if(TempConfig[0]->CreateDivineConfigurations)
    Configs = databasecreator<item>::CreateDivineConfigurations(this, TempConfig, Configs);

  /* Gum solution */

  if(TempConfig[0]->CreateLockConfigurations)
  {
    const item::database*const* KeyConfigData = key::ProtoType.GetConfigData();
    int KeyConfigSize = key::ProtoType.GetConfigSize();
    int OldConfigs = Configs;

    for(int c1 = 0; c1 < OldConfigs; ++c1)
      if(!TempConfig[c1]->IsAbstract)
      {
	int BaseConfig = TempConfig[c1]->Config;
	int NewConfig = BaseConfig | BROKEN_LOCK;
	itemdatabase* ConfigDataBase = new itemdatabase(*TempConfig[c1]);
	ConfigDataBase->InitDefaults(this, NewConfig);
	ConfigDataBase->PostFix << "with a broken lock";
	ConfigDataBase->Possibility = 0;
	TempConfig[Configs++] = ConfigDataBase;

	for(int c2 = 0; c2 < KeyConfigSize; ++c2)
	{
	  NewConfig = BaseConfig | KeyConfigData[c2]->Config;
	  ConfigDataBase = new itemdatabase(*TempConfig[c1]);
	  ConfigDataBase->InitDefaults(this, NewConfig);
	  ConfigDataBase->PostFix << "with ";

	  if(KeyConfigData[c2]->UsesLongAdjectiveArticle)
	    ConfigDataBase->PostFix << "an ";
	  else
	    ConfigDataBase->PostFix << "a ";

	  ConfigDataBase->PostFix << KeyConfigData[c2]->Adjective << " lock";
	  ConfigDataBase->Possibility = 0;
	  TempConfig[Configs++] = ConfigDataBase;
	}
      }
  }

  return Configs;
}

void item::Draw(blitdata& BlitData) const
{
  const int AF = GraphicData.AnimationFrames;
  const int F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1);
  const bitmap* P = GraphicData.Picture[F];

  if(BlitData.CustomData & ALLOW_ALPHA)
    P->AlphaLuminanceBlit(BlitData);
  else
    P->LuminanceMaskedBlit(BlitData);

  if(Fluid && ShowFluids())
    DrawFluids(BlitData);
}

v2 item::GetLargeBitmapPos(v2 BasePos, int I) const
{
  const int SquareIndex = I ? I / (GraphicData.AnimationFrames >> 2) : 0;
  return v2(SquareIndex & 1 ? BasePos.X + 16 : BasePos.X, SquareIndex & 2 ? BasePos.Y + 16 : BasePos.Y);
}

void item::LargeDraw(blitdata& BlitData) const
{
  const int TrueAF = GraphicData.AnimationFrames >> 2;
  const int SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK;
  const int F = !(BlitData.CustomData & ALLOW_ANIMATE) ? SquareIndex * TrueAF : SquareIndex * TrueAF + (GET_TICK() & (TrueAF - 1));
  const bitmap* P = GraphicData.Picture[F];

  if(BlitData.CustomData & ALLOW_ALPHA)
    P->AlphaLuminanceBlit(BlitData);
  else
    P->LuminanceMaskedBlit(BlitData);
}

void item::DonateIDTo(item* Item)
{
  game::RemoveItemID(Item->ID);
  game::UpdateItemID(Item, ID);
  Item->ID = ID;
  ID = 0;
}

void item::SignalRustLevelChange()
{
  SignalVolumeAndWeightChange();
  UpdatePictures();
  SendNewDrawAndMemorizedUpdateRequest();
}

const rawbitmap* item::GetRawPicture() const
{
  return igraph::GetRawGraphic(GetGraphicsContainerIndex());
}

void item::RemoveFluid(fluid* ToBeRemoved)
{
  truth WasAnimated = IsAnimated();
  truth HasFluids = false;

  for(int c = 0; c < SquaresUnder; ++c)
  {
    fluid* F = Fluid[c];

    if(F == ToBeRemoved)
      Fluid[c] = F->Next;
    else if(F)
    {
      fluid* LF = F;

      for(F = F->Next; F; LF = F, F = F->Next)
	if(F == ToBeRemoved)
	{
	  LF->Next = F->Next;
	  break;
	}
    }

    if(Fluid[c])
      HasFluids = true;
  }

  UpdatePictures();

  if(!HasFluids)
  {
    delete [] Fluid;
    Fluid = 0;

    if(!IsAnimated() != !WasAnimated && Slot[0]->IsVisible())
      GetSquareUnder()->DecStaticAnimatedEntities();
  }

  SignalEmitationDecrease(ToBeRemoved->GetEmitation());
  SignalVolumeAndWeightChange();
}

void item::AddFluid(liquid* ToBeAdded, festring LocationName, int SquareIndex, truth IsInside)
{
  truth WasAnimated = IsAnimated();

  if(Fluid)
  {
    fluid* F = Fluid[SquareIndex];

    if(!F)
      Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside);
    else
    {
      fluid* LF;

      do
      {
	if(ToBeAdded->IsSameAs(F->GetLiquid()))
	{
	  F->AddLiquidAndVolume(ToBeAdded->GetVolume());
	  delete ToBeAdded;
	  return;
	}

	LF = F;
	F = F->Next;
      }
      while(F);

      LF->Next = new fluid(ToBeAdded, this, LocationName, IsInside);
    }
  }
  else
  {
    Fluid = new fluid*[SquaresUnder];
    memset(Fluid, 0, SquaresUnder * sizeof(fluid*));
    Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside);
  }

  UpdatePictures();
  SignalVolumeAndWeightChange();
  SignalEmitationIncrease(ToBeAdded->GetEmitation());

  if(Slot[0])
  {
    if(!IsAnimated() != !WasAnimated && Slot[0]->IsVisible())
      GetSquareUnder()->IncStaticAnimatedEntities();

    SendNewDrawAndMemorizedUpdateRequest();
  }
}

void item::SendNewDrawAndMemorizedUpdateRequest() const
{
  if(!game::IsInWilderness())
    for(int c = 0; c < SquaresUnder; ++c)
      if(Slot[c])
      {
	lsquare* Square = GetLSquareUnder(c);
	Square->SendNewDrawRequest();
	Square->SendMemorizedUpdateRequest();
      }
}

void item::CalculateEmitation()
{
  object::CalculateEmitation();

  if(Fluid)
    for(int c = 0; c < SquaresUnder; ++c)
      for(const fluid* F = Fluid[c]; F; F = F->Next)
	game::CombineLights(Emitation, F->GetEmitation());
}

void item::FillFluidVector(fluidvector& Vector, int SquareIndex) const
{
  if(Fluid)
    for(fluid* F = Fluid[SquareIndex]; F; F = F->Next)
      Vector.push_back(F);
}

void item::SpillFluid(character*, liquid* Liquid, int SquareIndex)
{
  if(AllowFluids() && Liquid->GetVolume())
    AddFluid(Liquid, "", SquareIndex, false);
  else
    delete Liquid;
}

void item::TryToRust(long LiquidModifier)
{
  if(MainMaterial->TryToRust(LiquidModifier))
  {
    if(CanBeSeenByPlayer())
      if(MainMaterial->GetRustLevel() == NOT_RUSTED)
	ADD_MESSAGE("%s rusts.", CHAR_NAME(DEFINITE));
      else
	ADD_MESSAGE("%s rusts more.", CHAR_NAME(DEFINITE));

    MainMaterial->SetRustLevel(MainMaterial->GetRustLevel() + 1);
  }
}

void item::CheckFluidGearPictures(v2 ShadowPos, int SpecialFlags, truth BodyArmor)
{
  if(Fluid)
    for(fluid* F = Fluid[0]; F; F = F->Next)
      F->CheckGearPicture(ShadowPos, SpecialFlags, BodyArmor);
}

void item::DrawFluidGearPictures(blitdata& BlitData, int SpecialFlags) const
{
  if(Fluid)
    for(const fluid* F = Fluid[0]; F; F = F->Next)
      F->DrawGearPicture(BlitData, SpecialFlags);
}

void item::DrawFluidBodyArmorPictures(blitdata& BlitData, int SpecialFlags) const
{
  if(Fluid)
    for(const fluid* F = Fluid[0]; F; F = F->Next)
      F->DrawBodyArmorPicture(BlitData, SpecialFlags);
}

void item::DrawFluids(blitdata& BlitData) const
{
  const int SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK;

  for(const fluid* F = Fluid[SquareIndex]; F; F = F->Next)
    F->Draw(BlitData);
}

void item::ReceiveAcid(material*, const festring&, long Modifier)
{
  if(GetMainMaterial()->GetInteractionFlags() & CAN_DISSOLVE)
  {
    int Damage = Modifier / 1000;

    if(Damage)
    {
      Damage += RAND() % Damage;
      ReceiveDamage(0, Damage, ACID);
    }
  }
}

void item::DonateFluidsTo(item* Item)
{
  if(Fluid)
    for(int c = 0; c < GetSquaresUnder(); ++c)
      for(fluid* F = Fluid[c]; F; F = F->Next)
      {
	liquid* Liquid = F->GetLiquid();
	Item->AddFluid(Liquid->SpawnMoreLiquid(Liquid->GetVolume()), F->GetLocationName(), c, F->IsInside());
      }
}

void item::Destroy(character* Destroyer, int)
{
  if(CanBeSeenByPlayer())
    ADD_MESSAGE("%s is destroyed.", GetExtendedDescription().CStr());

  if(Destroyer && IsOnGround())
  {
    room* Room = GetRoom();

    if(Room)
      Room->HostileAction(Destroyer);
  }

  truth Equipped = PLAYER->Equips(this);
  RemoveFromSlot();
  SendToHell();

  if(Equipped)
    game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]"));
}

void item::RemoveRust()
{
  for(int c = 0; c < GetMaterials(); ++c)
    if(GetMaterial(c))
      GetMaterial(c)->SetRustLevel(NOT_RUSTED);
}

void item::SetSpoilPercentage(int Value)
{
  for(int c = 0; c < GetMaterials(); ++c)
  {
    material* Material = GetMaterial(c);

    if(Material && Material->CanSpoil())
      Material->SetSpoilCounter(Material->GetSpoilModifier() * Value / 100);
  }
}

void item::RedistributeFluids()
{
  if(Fluid)
    for(int c = 0; c < GetSquaresUnder(); ++c)
      for(fluid* F = Fluid[c]; F; F = F->Next)
	F->Redistribute();
}

material* item::GetConsumeMaterial(const character* Consumer, materialpredicate Predicate) const
{
  return (MainMaterial->*Predicate)() && Consumer->CanConsume(MainMaterial) ? MainMaterial : 0;
}

/* The parameter can only be MainMaterial */

material* item::RemoveMaterial(material*)
{
  RemoveFromSlot();
  SendToHell();
  return 0;
}

void item::InitMaterials(material* FirstMaterial, truth CallUpdatePictures)
{
  InitMaterial(MainMaterial, FirstMaterial, GetDefaultMainVolume());
  SignalVolumeAndWeightChange();

  if(CallUpdatePictures)
    UpdatePictures();
}

void item::GenerateMaterials()
{
  int Chosen = RandomizeMaterialConfiguration();
  const fearray<long>& MMC = GetMainMaterialConfig();
  InitMaterial(MainMaterial,
	       MAKE_MATERIAL(MMC.Data[MMC.Size == 1 ? 0 : Chosen]),
	       GetDefaultMainVolume());
}

void item::SignalSquarePositionChange(int Position)
{
  Flags &= ~SQUARE_POSITION_BITS;
  Flags |= Position << SQUARE_POSITION_SHIFT;
}

truth item::Read(character* Reader)
{
  Reader->StartReading(this, GetReadDifficulty());
  return true;
}

truth item::CanBeHardened(const character*) const
{
  return MainMaterial->GetHardenedMaterial(this) != NONE;
}

void item::SetLifeExpectancy(int Base, int RandPlus)
{
  LifeExpectancy = RandPlus > 1 ? Base + RAND_N(RandPlus) : Base;
  Enable();
}

truth item::IsVeryCloseToSpoiling() const
{
  for(int c = 0; c < GetMaterials(); ++c)
    if(GetMaterial(c) && !GetMaterial(c)->IsVeryCloseToSpoiling())
      return false;

  return true;
}

truth item::IsValuable() const
{
  if(DataBase->IsValuable)
    return true;

  for(int c = 0; c < GetMaterials(); ++c)
  {
    material* M = GetMaterial(c);

    if(M && M->GetCommonFlags() & IS_VALUABLE)
      return true;
  }

  return false;
}

int item::GetHinderVisibilityBonus(const character* Char) const
{
  int Bonus = 0;

  if(GetGearStates() & INFRA_VISION
     && !Char->TemporaryStateIsActivated(INFRA_VISION))
    Bonus += 20000;

  if(GetGearStates() & ESP
     && !Char->TemporaryStateIsActivated(ESP))
    Bonus += 20000;

  if(!game::IsDark(GetEmitation()))
    Bonus += 5000;

  return Bonus;
}

long item::GetFixPrice() const
{
  item* Clone = GetProtoType()->Clone(this);
  Clone = Clone->Fix();
  Clone->RemoveRust();
  long FixPrice = Clone->GetTruePrice();
  Clone->SendToHell();
  return Max(long(3.5 * sqrt(FixPrice)), 10L);
}

void item::AddTrapName(festring& String, int Amount) const
{
  if(Amount == 1)
    AddName(String, DEFINITE);
  else
  {
    String << Amount << ' ';
    AddName(String, PLURAL);
  }
}

truth item::WillSpoil() const
{
  for(int c = 0; c < GetMaterials(); ++c)
  {
    const material* Material = GetMaterial(c);

    if(Material && Material->Spoils())
      return true;
  }

  return false;
}

int item::GetMaxSpoilPercentage() const
{
  int MaxPercentage = 0;

  for(int c = 0; c < GetMaterials(); ++c)
  {
    const material* Material = GetMaterial(c);

    if(Material)
      MaxPercentage = Max(MaxPercentage, Material->GetSpoilPercentage());
  }

  return MaxPercentage;
}

truth item::HasPrice() const
{
  return GetPrice() || GetMaterialPrice();
}

void item::Disappear()
{
  RemoveFromSlot();
  SendToHell();
}

outputfile& operator<<(outputfile& SaveFile, const idholder* IdHolder)
{
  SaveFile << IdHolder->ID;
  return SaveFile;
}

inputfile& operator>>(inputfile& SaveFile, idholder*& IdHolder)
{
  IdHolder = new idholder(ReadType<ulong>(SaveFile));
  return SaveFile;
}

festring item::GetExtendedDescription() const
{
  if(!CanBeSeenByPlayer())
    return CONST_S("something");

  festring Desc;
  const character* Carrier = FindCarrier();

  if(Carrier)
  {
    if(Carrier->IsPlayer())
    {
      Desc << "your ";
      AddName(Desc, UNARTICLED);
      return Desc;
    }
    else if(Carrier->CanBeSeenByPlayer())
    {
      Carrier->AddName(Desc, DEFINITE);
      Desc << "'s ";
      AddName(Desc, UNARTICLED);
      return Desc;
    }
  }

  AddName(Desc, DEFINITE);

  if(IsOnGround())
    GetLSquareUnder()->AddLocationDescription(Desc);

  return Desc;
}

const character* item::FindCarrier() const
{
  return Slot[0]->FindCarrier();
}
Apr 11, 2010, 6:32 am
#2
Joined: Jan 25, 2008
Occupation: Turns out I can't have a cool avatar, I'll just put crap in it.
Location: 48kg / 105 lbs
Interests: My name is Failbert
Posts: 697
(You could post which files to look up in the source, instead of pasting it all here, I think.)

An item's block value seems to be calculated by the formula
((10000. / (1000 + GetWeight()) + GetTHVBonus()) * GetBlockModifier() / 10000.)
where:
GetWeight is weight (based on material mostly, it seems),
GetTHVBonus is a bonus probably derived from skill level,
GetBlockModifier is an interesting piece, based on your Size times "Roundness" (a shield has 95, a banana has 15). The resulting value is then shifted with "<<". I don't know, however, how exactly "<<" works, but it MAY BE that roundness is multiplied 2^2 for shields, and 2^1 for non-shields.

Char.cpp seems to hold more on using that value, though.
Well I assume that a bolt has a given, numeric, amouunt of perwer. (assi in enegri)... unless it's LAERSER SKISKLS POEWN PEWN!
Apr 11, 2010, 8:00 pm
#3
Joined: Dec 3, 2007
Occupation: Chaos Weaver
Location: Standing between all life and death
Posts: 2,898
Thanks! That helps a lot. By the way, where did you find those roundness values?
Apr 12, 2010, 9:37 am
#4
Master mine stomper


Joined: Dec 16, 2007
Occupation: Shoveling. But metal.
Location: Blazing in the steppes
Interests: Absolutely fuck-all.
Posts: 2,050
Sheesh, at least put it in the spoiler tag...
Apr 12, 2010, 11:00 am
#5
Joined: Jan 25, 2008
Occupation: Turns out I can't have a cool avatar, I'll just put crap in it.
Location: 48kg / 105 lbs
Interests: My name is Failbert
Posts: 697
It's like the only page of Attnam eligible for widescreen.
Well I assume that a bolt has a given, numeric, amouunt of perwer. (assi in enegri)... unless it's LAERSER SKISKLS POEWN PEWN!
Jump to