using System;
using System.Collections.Generic;
namespace Classes
{
public struct StatValue : IDataIO
{
int[, ,] raceSexMinMax;
int[] classMin;
int[] ageEffects;
public StatValue(int[, ,] _raceSexMinMax, int[] _classMin, int[] _ageEffects)
{
raceSexMinMax = _raceSexMinMax;
classMin = _classMin;
ageEffects = _ageEffects;
cur = full = 0;
}
public int cur;
public int full;
public void Load(int val)
{
full = cur = val;
}
public void Assign(StatValue sv)
{
full = sv.full;
cur = sv.cur;
}
public void Inc()
{
full += 1;
cur += 1;
}
public void Dec()
{
full -= 1;
cur -= 1;
}
public void EnforceRaceSexLimits(int race, int sex)
{
if( raceSexMinMax != null )
{
full = Math.Min(raceSexMinMax[race, 1, sex], full);
full = Math.Max(raceSexMinMax[race, 0, sex], full);
}
cur = full;
}
public void EnforceClassLimits(int _class)
{
if (classMin != null)
{
full = Math.Max(classMin[_class], full);
}
cur = full;
}
public void AgeEffects(int race, int age)
{
for (int i = 0; i < 5; i++)
{
if (Limits.RaceAgeBrackets[race, i] < age)
{
full += ageEffects[i];
}
}
}
public void Write(byte[] data, int offset)
{
data[offset + 0] = (byte)cur;
data[offset + 1] = (byte)full;
}
public void Read(byte[] data, int offset)
{
// enforce values in valid range
cur = Math.Min((int)data[offset + 0], 25);
full = Math.Min((int)data[offset + 1], 25);
}
public override string ToString()
{
return string.Format("{0}/{1}", cur, full);
}
}
public class PlayerStats : IDataIO
{
public StatValue Str = new StatValue(Limits.StrRaceSexMinMax, Limits.StrClassMin, Limits.StrAgeEffect);
public StatValue Str00 = new StatValue(Limits.Str00RaceSexMinMax, Limits.Str00ClassMin, Limits.Str00AgeEffect);
public StatValue Con = new StatValue(Limits.ConRaceSexMinMax, Limits.ConClassMin, Limits.ConAgeEffect);
public StatValue Dex = new StatValue(Limits.DexRaceSexMinMax, Limits.DexClassMin, Limits.DexAgeEffect);
public StatValue Int = new StatValue(Limits.IntRaceSexMinMax, Limits.IntClassMin, Limits.IntAgeEffect);
public StatValue Wis = new StatValue(Limits.WisRaceSexMinMax, Limits.WisClassMin, Limits.WisAgeEffect);
public StatValue Cha = new StatValue(Limits.ChaRaceSexMinMax, Limits.ChaClassMin, Limits.ChaAgeEffect);
void IDataIO.Write(byte[] data, int offset)
{
Str.Write(data, offset + 0x00);
Int.Write(data, offset + 0x02);
Wis.Write(data, offset + 0x04);
Dex.Write(data, offset + 0x06);
Con.Write(data, offset + 0x08);
Cha.Write(data, offset + 0x0a);
Str00.Write(data, offset + 0x0c);
}
void IDataIO.Read(byte[] data, int offset)
{
Str.Read(data, offset + 0x00);
Int.Read(data, offset + 0x02);
Wis.Read(data, offset + 0x04);
Dex.Read(data, offset + 0x06);
Con.Read(data, offset + 0x08);
Cha.Read(data, offset + 0x0a);
Str00.Read(data, offset + 0x0c);
}
public void Assign(PlayerStats ps)
{
Str.Assign(ps.Str);
Int.Assign(ps.Int);
Wis.Assign(ps.Wis);
Dex.Assign(ps.Dex);
Con.Assign(ps.Con);
Cha.Assign(ps.Cha);
Str00.Assign(ps.Str00);
}
public StatValue this[int idx]
{
get
{
switch (idx)
{
case 0: return Str;
case 1: return Int;
case 2: return Wis;
case 3: return Dex;
case 4: return Con;
case 5: return Cha;
default: throw new IndexOutOfRangeException(string.Format("idx {0} not in [0-5]", idx));
}
}
}
public override string ToString()
{
return string.Format("{0} ({6}),{1},{2},{3},{4},{5}", Str, Int, Wis, Con, Dex, Cha, Str00);
}
}
public struct ClassLevels
{
}
public class ActiveItems
{
const int ItemSlots = 13;
Item[] itemArray = new Item[ItemSlots]; // 0x151[]
public byte PrimaryWeaponHandCount()
{
if (primaryWeapon == null) return 0;
return primaryWeapon.HandsCount();
}
public byte SecondaryWeaponHandCount()
{
if (secondaryWeapon == null) return 0;
return secondaryWeapon.HandsCount();
}
public Item primaryWeapon // field_151
{// 0x151
get { return itemArray[0]; }
set { itemArray[0] = value; }
}
public Item secondaryWeapon // field_155
{ // 0x155
get { return itemArray[1]; }
set { itemArray[1] = value; }
}
public Item armor // field_159
{ // 0x159
get { return itemArray[2]; }
set { itemArray[2] = value; }
}
public Item field_15D
{ // 0x15D
get { return itemArray[3]; }
set { itemArray[3] = value; }
}
public Item field_161
{ // 0x161
get { return itemArray[4]; }
set { itemArray[4] = value; }
}
public Item field_165
{ // 0x165
get { return itemArray[5]; }
set { itemArray[5] = value; }
}
public Item field_169
{ // 0x169
get { return itemArray[6]; }
set { itemArray[6] = value; }
}
public Item field_16D
{ // 0x16D
get { return itemArray[7]; }
set { itemArray[7] = value; }
}
public Item field_171
{ // 0x171
get { return itemArray[8]; }
set { itemArray[8] = value; }
}
public Item Item_ptr_01
{ // 0x175
get { return itemArray[9]; }
set { itemArray[9] = value; }
}
public Item Item_ptr_02
{// 0x179
get { return itemArray[10]; }
set { itemArray[10] = value; }
}
public Item arrows
{// 0x17d
get { return itemArray[11]; }
set { itemArray[11] = value; }
}
public Item quarrels
{ // 0x181
get { return itemArray[12]; }
set { itemArray[12] = value; }
}
public Item this[ItemSlot slot]
{
get
{
return itemArray[(int)slot];
}
set
{
itemArray[(int)slot] = value;
}
}
public void Reset()
{
for (int slot = 0; slot < ItemSlots; slot++)
{
itemArray[slot] = null;
}
}
public void UndreadyAll(int classFlags)
{
for (int item_slot = 0; item_slot < ItemSlots; item_slot++)
{
if (itemArray[item_slot] != null &&
(gbl.ItemDataTable[itemArray[item_slot].type].classFlags & classFlags) == 0 &&
itemArray[item_slot].cursed == false)
{
itemArray[item_slot].readied = false;
}
}
}
}
public class Control
{
public const byte PC_Base = 0;
public const byte PC_Mask = 0x7F;
public const byte NPC_Base = 0x80;
public const byte NPC_Berzerk = 0xB2;
public const byte PC_Berzerk = 0xB3;
}
///
/// Summary description for Player.
///
public class Player
{
[DataOffset(0x00, DataType.PString, 15)]
public string name;
//[DataOffset(0x10, DataType.Cust1Array, 6)]
//public StatValue[] stats;
[DataOffset(0x10, DataType.CustSaveLoad, 0x12)]
public PlayerStats stats2;
public SpellList spellList; // ox1e was spell_list
[DataOffset(0x72, DataType.Byte)]
public byte spell_to_learn_count; // 0x72;
[DataOffset(0x73, DataType.SByte)]
public sbyte thac0; // 0x73; field_73
[DataOffset(0x74, DataType.IByte)]
public Race race; // 0x74;
[DataOffset(0x75, DataType.IByte)]
public ClassId _class; // 0x75;
[DataOffset(0x76, DataType.SWord)]
public short age; // 0x76;
[DataOffset(0x78, DataType.Byte)]
public byte hit_point_max; // 0x78;
[DataOffset(0x79, DataType.ByteArray, 100)]
public byte[] spellBook = new byte[100]; //78[di]; // 1- 100 or 0x79 - 0xDC
public bool KnowsSpell(Spells spell) { return spellBook[(int)spell - 1] != 0; }
public void LearnSpell(Spells spell) { spellBook[(int)spell - 1] = 1; }
[DataOffset(0xdd, DataType.Byte)]
public byte attackLevel; // 0xdd; field_DD
[DataOffset(0xde, DataType.Byte)]
public byte field_DE; // 0xde;
[DataOffset(0xdf, DataType.ByteArray, 5)]
public byte[] saveVerse = new byte[5]; // 0xdf; field_DF
[DataOffset(0xe4, DataType.Byte)]
public byte base_movement; // 0xe4;
[DataOffset(0xe5, DataType.Byte)]
public byte HitDice; // 0xe5; HitDice?
[DataOffset(0xe6, DataType.Byte)]
public byte multiclassLevel; // 0xe6;
[DataOffset(0xe7, DataType.Byte)]
public byte lost_lvls; // 0xe7;
[DataOffset(0xe8, DataType.Byte)]
public byte lost_hp; // 0xe8;
[DataOffset(0xe9, DataType.Byte)]
public byte field_E9; // 0xe9;
[DataOffset(0xeA, DataType.ByteArray, 8)]
public byte[] thief_skills = new byte[8]; // 0xeA; [] was 1 offset @ 0xe9, pick_pockets, open_locks, find_remove_traps, move_silently, hide_in_shadows, hear_noise, climb_walls, read_languages
public List affects; // f2 - affect_ptr
[DataOffset(0xf6, DataType.Byte)]
public byte field_F6; // 0xf6; field_F6
[DataOffset(0xf7, DataType.Byte)]
public byte control_morale; // 0xf7; field_F7
[DataOffset(0xf8, DataType.Byte)]
public byte npcTreasureShareCount; // 0xf8;
[DataOffset(0xf9, DataType.Byte)]
public byte field_F9; // 0xf9
[DataOffset(0xfa, DataType.Byte)]
public byte field_FA; // 0xfa;
[DataOffset(0xfb, DataType.ShortArray, 7)]
public MoneySet Money;
[DataOffset(0x109, DataType.ByteArray, 8)]
public byte[] ClassLevel = new byte[8]; /* Skill_A_lvl */
public byte cleric_lvl // 0x109;
{
get { return ClassLevel[0]; }
set { ClassLevel[0] = value; }
}
public byte druid_lvl // 0x10a;
{
get { return ClassLevel[1]; }
set { ClassLevel[1] = value; }
}
public byte fighter_lvl // 0x10b;
{
get { return ClassLevel[2]; }
set { ClassLevel[2] = value; }
}
public byte paladin_lvl // 0x10c;
{
get { return ClassLevel[3]; }
set { ClassLevel[3] = value; }
}
public byte ranger_lvl // 0x10d;
{
get { return ClassLevel[4]; }
set { ClassLevel[4] = value; }
}
public byte magic_user_lvl // 0x10e;
{
get { return ClassLevel[5]; }
set { ClassLevel[5] = value; }
}
public byte thief_lvl // 0x10f;
{
get { return ClassLevel[6]; }
set { ClassLevel[6] = value; }
}
public byte monk_lvl // 0x110;
{
get { return ClassLevel[7]; }
set { ClassLevel[7] = value; }
}
[DataOffset(0x111, DataType.ByteArray, 8)]
public byte[] ClassLevelsOld = new byte[8];
public byte cleric_old_lvl // 0x111;
{
get { return ClassLevelsOld[0]; }
set { ClassLevelsOld[0] = value; }
}
//public byte druid_old_lvl // 0x112;
//{
// get { return ClassLevelsOld[1]; }
// set { ClassLevelsOld[1] = value; }
//}
public byte fighter_old_lvl // 0x113;
{
get { return ClassLevelsOld[2]; }
set { ClassLevelsOld[2] = value; }
}
public byte paladin_old_lvl // 0x114;
{
get { return ClassLevelsOld[3]; }
set { ClassLevelsOld[3] = value; }
}
public byte ranger_old_lvl // 0x115;
{
get { return ClassLevelsOld[4]; }
set { ClassLevelsOld[4] = value; }
}
public byte magic_user_old_lvl // 0x116;
{
get { return ClassLevelsOld[5]; }
set { ClassLevelsOld[5] = value; }
}
public byte thief_old_lvl // 0x117;
{
get { return ClassLevelsOld[6]; }
set { ClassLevelsOld[6] = value; }
}
//public byte monk_old_level // 0x118;
//{
// get { return ClassLevelsOld[7]; }
// set { ClassLevelsOld[7] = value; }
//}
public int SkillLevel(SkillType skill)
{
return ClassLevel[(int)skill] + (ClassLevelsOld[(int)skill] * DualClassExceedsPreviousLevel());
}
[DataOffset(0x119, DataType.Byte)]
public byte sex; // 0x119;
[DataOffset(0x11a, DataType.IByte)]
public MonsterType monsterType; // 0x11a;
[DataOffset(0x11b, DataType.Byte)]
public byte alignment; // 0x11b;
///
/// half-attacks count
///
[DataOffset(0x11c, DataType.Byte)]
public byte attacksCount; // 0x11c;
[DataOffset(0x11d, DataType.Byte)]
public byte baseHalfMoves; // 0x11d;
[DataOffset(0x11e, DataType.Byte)]
public byte attack1_DiceCountBase; // 0x11e;
[DataOffset(0x11f, DataType.Byte)]
public byte attack2_DiceCountBase; // 0x11f;
[DataOffset(0x120, DataType.Byte)]
public byte attack1_DiceSizeBase; // 0x120;
[DataOffset(0x121, DataType.Byte)]
public byte attack2_DiceSizeBase; // 0x121;
[DataOffset(0x122, DataType.Byte)]
public byte attack1_DamageBonusBase; // 0x122;
[DataOffset(0x123, DataType.Byte)]
public byte attack2_DamageBonusBase; // 0x123;
[DataOffset(0x124, DataType.Byte)]
public byte base_ac; // 0x124;
[DataOffset(0x125, DataType.Byte)]
public byte field_125; // 0x125;
[DataOffset(0x126, DataType.Byte)]
public byte mod_id; // 0x126; field_126
[DataOffset(0x127, DataType.Int)]
public int exp; // 0x127
[DataOffset(0x12b, DataType.Byte)]
public byte classFlags; // 0x12b;
[DataOffset(0x12c, DataType.Byte)]
public byte hit_point_rolled; // 0x12c;
//[DataOffset(0x12d, DataType.ByteArray,15)]
public byte[,] spellCastCount = new byte[3, 5]; // 0x12d - field_12D
[DataOffset(0x13c, DataType.SWord)]
public short field_13C; // 0x13c
[DataOffset(0x13e, DataType.Byte)]
public byte field_13E; // 0x13e;
[DataOffset(0x13f, DataType.Byte)]
public byte field_13F; // 0x13f;
[DataOffset(0x140, DataType.Byte)]
public byte field_140; // 0x140;
[DataOffset(0x141, DataType.Byte)]
public byte head_icon; // 0x141;
[DataOffset(0x142, DataType.Byte)]
public byte weapon_icon; // 0x142;
[DataOffset(0x143, DataType.Byte)]
public byte icon_id; // 0x143;
[DataOffset(0x144, DataType.Byte)]
public byte icon_size; // 0x144; field_144 1 small 2 normal
[DataOffset(0x145, DataType.ByteArray, 6)]
public byte[] icon_colours = new byte[6]; // 0x145 = field_144[1] // byte[6]
[DataOffset(0x14b, DataType.Byte)]
public byte field_14B; // 0x14b;
//[DataOffset(0x14c, DataType.Byte)]
//public byte field_14C; // 0x14c; // items.Count
public const int MaxItems = 16;
public List- items; // 0x14d
//public Item itemsPtr; // 0x14d
public ActiveItems activeItems = new ActiveItems();
[DataOffset(0x185, DataType.Byte)]
public byte weaponsHandsUsed; // 0x185;
[DataOffset(0x186, DataType.SByte)]
public sbyte field_186; // 0x186;
[DataOffset(0x187, DataType.SWord)]
public short weight; // 0x187
public Action actions; // 0x18d
[DataOffset(0x191, DataType.Byte)]
public byte paladinCuresLeft; // 0x191 field_191
[DataOffset(0x192, DataType.Byte)]
public byte field_192; // 0x192
[DataOffset(0x193, DataType.Byte)]
public byte field_193; // 0x193
[DataOffset(0x194, DataType.Byte)]
public byte field_194; // 0x194
[DataOffset(0x195, DataType.IByte)]
public Status health_status; // 0x195
[DataOffset(0x196, DataType.Bool)]
public bool in_combat; // 0x196
[DataOffset(0x197, DataType.IByte)]
public CombatTeam combat_team; // 0x197 0 - our team, 1 - enemy
[DataOffset(0x198, DataType.IByte)]
public QuickFight quick_fight; // 0x198
[DataOffset(0x199, DataType.IByte)]
public int hitBonus; // 0x199 field_199
[DataOffset(0x19a, DataType.Byte)]
public byte ac; // 0x19a
public int DisplayAc { get { return 0x3C - ac; } }
[DataOffset(0x19b, DataType.Byte)]
public byte ac_behind; // 0x19b field_19B
public byte AttacksLeft(int index)
{
switch (index)
{
case 1:
return attack1_AttacksLeft;
case 2:
return attack2_AttacksLeft;
default:
throw new System.NotImplementedException();
}
}
public void AttacksLeftSet(int index, byte value)
{
switch (index)
{
case 1:
attack1_AttacksLeft = value;
break;
case 2:
attack2_AttacksLeft = value;
break;
default:
throw new System.NotImplementedException();
}
}
public void AttacksLeftDec(int index)
{
switch (index)
{
case 1:
attack1_AttacksLeft -= 1;
break;
case 2:
attack2_AttacksLeft -= 1;
break;
default:
throw new System.NotImplementedException();
}
}
[DataOffset(0x19c, DataType.Byte)]
public byte attack1_AttacksLeft; // 0x19c - field_19C
[DataOffset(0x19d, DataType.Byte)]
public byte attack2_AttacksLeft; // 0x19d - field_19D
public byte attackDiceCount(int index)
{
switch (index)
{
case 1:
return attack1_DiceCount;
case 2:
return attack2_DiceCount;
default:
throw new System.NotImplementedException();
}
}
[DataOffset(0x19E, DataType.Byte)]
public byte attack1_DiceCount; // 0x19e field_19E
[DataOffset(0x19F, DataType.Byte)]
public byte attack2_DiceCount; // 0x19f
public byte attackDiceSize(int index)
{
switch (index)
{
case 1:
return attack1_DiceSize;
case 2:
return attack2_DiceSize;
default:
throw new System.NotImplementedException();
}
}
[DataOffset(0x1A0, DataType.Byte)]
public byte attack1_DiceSize; // 0x1a0 field_1A0
[DataOffset(0x1A1, DataType.Byte)]
public byte attack2_DiceSize; // 0x1a1
public byte attackDamageBonus(int index)
{
switch (index)
{
case 1:
return (byte)attack1_DamageBonus;
case 2:
return attack2_DamageBonus;
default:
throw new System.NotImplementedException();
}
}
[DataOffset(0x1a2, DataType.SByte)]
public sbyte attack1_DamageBonus; // 0x1a2
[DataOffset(0x1a3, DataType.Byte)]
public byte attack2_DamageBonus; // 0x1a3
[DataOffset(0x1a4, DataType.Byte)]
public byte hit_point_current; // 0x1a4
[DataOffsetAttribute(0x1A5, DataType.Byte)]
public byte movement; // 0x1a5 initiative
public const int StructSize = 0x1A6;
public Player()
{
Init();
}
public Player(byte[] data, int offset)
{
Init();
DataIO.ReadObject(this, data, offset);
spellList.Load(data, offset + 0x1e);
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
spellCastCount[i, j] = data[0x12d + j + (i * i)];
}
}
}
private void Init()
{
spellCastCount = new byte[3, 5];
//stats = new StatValue[6];
stats2 = new PlayerStats();
name = string.Empty;
items = new List
- ();
affects = new List();
actions = null;
Money = new MoneySet();
spellList = new SpellList();
}
public Player ShallowClone()
{
Player p = (Player)this.MemberwiseClone();
p.stats2.Assign(this.stats2);
return p;
}
public byte[] ToByteArray()
{
byte[] data = new byte[StructSize];
DataIO.WriteObject(this, data);
spellList.Save(data, 0x1e);
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
data[0x12d + j + (i * i)] = spellCastCount[i, j];
}
}
return data;
}
public override string ToString()
{
return name;
}
public bool CanDuelClass()
{
if (race != Race.human)
{
return false;
}
for (ClassId index = ClassId.cleric; index <= ClassId.monk; index++)
{
if (ClassLevelsOld[(int)index] > 0)
{
return false;
}
}
return true;
}
int DualClassExceedsPreviousLevel() // sub_6B3D1
{
if (DuelClassCurrentLevel() > multiclassLevel)
{
return 1;
}
else
{
return 0;
}
}
int DuelClassCurrentLevel()
{
if (race != Race.human)
{
return 0;
}
int loop_var = 0;
while (loop_var < 7 &&
ClassLevel[loop_var] == 0)
{
loop_var++;
}
return ClassLevel[loop_var];
}
public CombatTeam OppositeTeam()
{
return (combat_team == CombatTeam.Ours) ? CombatTeam.Enemy : CombatTeam.Ours;
}
public bool HasAffect(Affects type)
{
return affects.Exists(aff => aff.type == type);
}
public Affect GetAffect(Affects type)
{
return affects.Find(aff => aff.type == type);
}
static Affects[] held_affects = { Affects.snake_charm, Affects.paralyze, Affects.sleep, Affects.helpless };
public bool IsHeld()
{
return Array.Exists(held_affects, affect => HasAffect(affect));
}
public void RemoveWeight(int amount)
{
weight -= (short)amount;
}
public void AddWeight(int amount)
{
weight += (short)amount;
}
}
}