Search Results
Searched for posts by chaostrom in all forums

Showing results 661 - 670 out of 1248 total
Modify your search
Posted by chaostrom, Apr 29, 2010 at 10:00 pm
Unbreakable light source.
Posted by chaostrom, Apr 12, 2010 at 9:28 pm
Yes. As Kahvi said, development can continue so long as there's even one dedicated coder. Sure, in the beginning we had plenty of volunteers, but none dedicated, and they've all gone somewhere it seems. Seems a bit much for cap to have to do it alone though.
Posted by chaostrom, Apr 11, 2010 at 8:00 pm
Thanks! That helps a lot. By the way, where did you find those roundness values?
Posted by chaostrom, Apr 11, 2010 at 7:57 pm
Haha, pet Xinroch. Just awesome.
Posted by chaostrom, Apr 11, 2010 at 1:24 am
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();
}
Posted by chaostrom, Apr 7, 2010 at 8:48 am
At least you didn't lose your best ever character to blue screen...

Curse you Bill!
Posted by chaostrom, Mar 25, 2010 at 7:49 pm
About setting off kamikaze dwarves, hitting them from a distance with a wand of fireball does wonders, especially if said dwarf is surrounded by other enemies.
Posted by chaostrom, Mar 24, 2010 at 7:46 pm
Well done. That's the furthest I've ever gotten legit myself. GC6 is deadly if you're not prepared.
Posted by chaostrom, Mar 11, 2010 at 4:47 am
That's why you never try to humiliate your opponent with a forced blowjob.
Posted by chaostrom, Feb 11, 2010 at 5:29 am
Is that a screenshot from 0.50 though? The default weapon for a skeleton is a bronze axe -2 (with slight variations). Those enchantments are almost preposterous.