aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorfusion32 <marcopuzziello@gmail.com>2025-05-28 00:39:29 -0300
committerfusion32 <marcopuzziello@gmail.com>2025-05-28 00:39:29 -0300
commit5505f28c358a50e9d610784303485cc757459162 (patch)
tree7756da65dde0d3c125ae71f2ce2afdeb7dc8cd54 /src
parent42959207914208438261810757abea43422a28fe (diff)
downloadgame-5505f28c358a50e9d610784303485cc757459162.tar.gz
game-5505f28c358a50e9d610784303485cc757459162.zip
finish objects.cc
Diffstat (limited to 'src')
-rw-r--r--src/enums.hh30
-rw-r--r--src/main.cc5
-rw-r--r--src/map.cc5
-rw-r--r--src/map.hh1
-rw-r--r--src/objects.cc487
-rw-r--r--src/objects.hh19
-rw-r--r--src/stubs.hh3
7 files changed, 521 insertions, 29 deletions
diff --git a/src/enums.hh b/src/enums.hh
index ea0d87a..f7aee3a 100644
--- a/src/enums.hh
+++ b/src/enums.hh
@@ -324,6 +324,36 @@ enum Skill: int {
SKILL_SOUL = 22,
};
+enum SPECIALMEANING: int {
+ MONEY_ONE = 1,
+ MONEY_HUNDRED = 2,
+ MONEY_TENTHOUSAND = 3,
+ INVENTORY_RIGHTHAND = 10,
+ INVENTORY_LEFTHAND = 11,
+ INVENTORY_BODY_MALE = 12,
+ INVENTORY_BODY_FEMALE = 13,
+ INVENTORY_CONTAINER = 14,
+ INVENTORY_FOOD = 15,
+ DEPOT_LOCKER = 20,
+ DEPOT_CHEST = 21,
+ PARCEL_NEW = 22,
+ PARCEL_STAMPED = 23,
+ PARCEL_LABEL = 24,
+ LETTER_NEW = 25,
+ LETTER_STAMPED = 26,
+ BLOOD_SPLASH = 30,
+ BLOOD_POOL = 31,
+ RUNE_BLANK = 40,
+ MAGICFIELD_FIRE_DANGEROUS = 41,
+ MAGICFIELD_FIRE_HARMLESS = 42,
+ MAGICFIELD_POISON_DANGEROUS = 43,
+ MAGICFIELD_POISON_HARMLESS = 44,
+ MAGICFIELD_ENERGY_DANGEROUS = 45,
+ MAGICFIELD_ENERGY_HARMLESS = 46,
+ MAGICFIELD_MAGICWALL = 47,
+ MAGICFIELD_RUSHWOOD = 48,
+};
+
enum SpellShapeType: int {
SHAPE_ACTOR = 0,
SHAPE_VICTIM = 1,
diff --git a/src/main.cc b/src/main.cc
index e0cf156..ecdb333 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -1,6 +1,7 @@
#include "common.hh"
#include "config.hh"
#include "map.hh"
+#include "objects.hh"
#include "stubs.hh"
@@ -227,7 +228,7 @@ static void InitAll(void){
//InitStrings();
//InitWriter();
//InitReader();
- //InitObjects();
+ InitObjects();
InitMap();
//InitInfo();
//InitMoveUse();
@@ -253,7 +254,7 @@ static void ExitAll(void){
//ExitInfo();
//ExitHouses();
ExitMap(SaveMapOn);
- //ExitObjects();
+ ExitObjects();
//ExitReader();
//ExitWriter();
//ExitStrings();
diff --git a/src/map.cc b/src/map.cc
index a5b898d..34c1998 100644
--- a/src/map.cc
+++ b/src/map.cc
@@ -1830,9 +1830,7 @@ Object CreateObject(void){
return Object(NextObjectID);
}
-// TODO(fusion): This is only used from `DeleteObject`. Should probably be an
-// internal helper.
-void DestroyObject(Object Obj){
+static void DestroyObject(Object Obj){
if(!Obj.exists()){
error("DestroyObject: Übergebenes Objekt existiert nicht.\n");
return;
@@ -1972,6 +1970,7 @@ void ChangeObject(Object Obj, INSTANCEATTRIBUTE Attribute, uint32 Value){
Obj.setAttribute(Attribute, Value);
}
+// TODO(fusion): This probably belongs to `operate.cc`.
void ChangeObject(Object Obj, ObjectType NewType, uint32 Value){
if(!Obj.exists()){
error("ChangeObject: Übergebenes Objekt existiert nicht (1, NewType=%d).\n", NewType.TypeID);
diff --git a/src/map.hh b/src/map.hh
index 3ab919d..6db6773 100644
--- a/src/map.hh
+++ b/src/map.hh
@@ -129,7 +129,6 @@ void ExitMap(bool Save);
// NOTE(fusion): Object related functions.
TObject *AccessObject(Object Obj);
Object CreateObject(void);
-void DestroyObject(Object Obj);
void DeleteObject(Object Obj);
void ChangeObject(Object Obj, ObjectType NewType);
void ChangeObject(Object Obj, INSTANCEATTRIBUTE Attribute, uint32 Value);
diff --git a/src/objects.cc b/src/objects.cc
index de62aca..e6903cd 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -1,8 +1,17 @@
#include "objects.hh"
+#include "config.hh"
#include "containers.hh"
+#include "script.hh"
+
+#include "stubs.hh"
+
+#include <fstream>
static vector<TObjectType> ObjectTypes(0, 5000, 1000);
static ObjectType SpecialObjects[49];
+static uint8 OldGroup[8192];
+static uint8 OldNumber[8192];
+static int NewType[65536];
static FLAG TypeAttributeFlags[62] = {
BANK, // WAYPOINTS
@@ -90,6 +99,140 @@ static FLAG InstanceAttributeFlags[18] = {
WEAROUT, // REMAININGUSES
};
+static const char FlagNames[66][30] = {
+ "Bank" // BANK
+ "Clip" // CLIP
+ "Bottom", // BOTTOM
+ "Top", // TOP
+ "Container", // CONTAINER
+ "Chest", // CHEST
+ "Cumulative", // CUMULATIVE
+ "UseEvent", // USEEVENT
+ "ChangeUse", // CHANGEUSE
+ "ForceUse", // FORCEUSE
+ "MultiUse", // MULTIUSE
+ "DistUse", // DISTUSE
+ "MovementEvent", // MOVEMENTEVENT
+ "CollisionEvent", // COLLISIONEVENT
+ "SeparationEvent", // SEPARATIONEVENT
+ "Key", // KEY
+ "KeyDoor", // KEYDOOR
+ "NameDoor", // NAMEDOOR
+ "LevelDoor", // LEVELDOOR
+ "QuestDoor", // QUESTDOOR
+ "Bed", // BED
+ "Food", // FOOD
+ "Rune", // RUNE
+ "Information", // INFORMATION
+ "Text", // TEXT
+ "Write", // WRITE
+ "WriteOnce", // WRITEONCE
+ "LiquidContainer", // LIQUIDCONTAINER
+ "LiquidSource", // LIQUIDSOURCE
+ "LiquidPool", // LIQUIDPOOL
+ "TeleportAbsolute", // TELEPORTABSOLUTE
+ "TeleportRelative", // TELEPORTRELATIVE
+ "Unpass", // UNPASS
+ "Unmove", // UNMOVE
+ "Unthrow", // UNTHROW
+ "Unlay", // UNLAY
+ "Avoid", // AVOID
+ "MagicField", // MAGICFIELD
+ "RestrictLevel", // RESTRICTLEVEL
+ "RestrictProfession", // RESTRICTPROFESSION
+ "Take", // TAKE
+ "Hang", // HANG
+ "HookSouth", // HOOKSOUTH
+ "HookEast", // HOOKEAST
+ "Rotate", // ROTATE
+ "Destroy", // DESTROY
+ "Clothes", // CLOTHES
+ "SkillBoost", // SKILLBOOST
+ "Protection", // PROTECTION
+ "Light", // LIGHT
+ "RopeSpot", // ROPESPOT
+ "Corpse", // CORPSE
+ "Expire", // EXPIRE
+ "ExpireStop", // EXPIRESTOP
+ "WearOut", // WEAROUT
+ "Weapon", // WEAPON
+ "Shield", // SHIELD
+ "Bow", // BOW
+ "Throw", // THROW
+ "Wand", // WAND
+ "Ammo", // AMMO
+ "Armor", // ARMOR
+ "Height", // HEIGHT
+ "Disguise", // DISGUISE
+ "ShowDetail", // SHOWDETAIL
+ "Special", // SPECIALOBJECT
+};
+
+static const char TypeAttributeNames[62][30] = {
+ "Waypoints", // WAYPOINTS
+ "Capacity", // CAPACITY
+ "ChangeTarget", // CHANGETARGET
+ "KeydoorTarget", // KEYDOORTARGET
+ "NamedoorTarget", // NAMEDOORTARGET
+ "LeveldoorTarget", // LEVELDOORTARGET
+ "QuestdoorTarget", // QUESTDOORTARGET
+ "Nutrition", // NUTRITION
+ "InformationType", // INFORMATIONTYPE
+ "FontSize", // FONTSIZE
+ "MaxLength", // MAXLENGTH
+ "MaxLengthOnce", // MAXLENGTHONCE
+ "SourceLiquidType", // SOURCELIQUIDTYPE
+ "AbsTeleportEffect", // ABSTELEPORTEFFECT
+ "RelTeleportDisplacement", // RELTELEPORTDISPLACEMENT
+ "RelTeleportEffect", // RELTELEPORTEFFECT
+ "AvoidDamageTypes", // AVOIDDAMAGETYPES
+ "MinimumLevel", // MINIMUMLEVEL
+ "Professions", // PROFESSIONS
+ "Weight", // WEIGHT
+ "RotateTarget", // ROTATETARGET
+ "DestroyTarget", // DESTROYTARGET
+ "BodyPosition", // BODYPOSITION
+ "SkillNumber", // SKILLNUMBER
+ "SkillModification", // SKILLMODIFICATION
+ "ProtectionDamageTypes", // PROTECTIONDAMAGETYPES
+ "DamageReduction", // DAMAGEREDUCTION
+ "Brightness", // BRIGHTNESS
+ "LightColor", // LIGHTCOLOR
+ "CorpseType", // CORPSETYPE
+ "TotalExpireTime", // TOTALEXPIRETIME
+ "ExpireTarget", // EXPIRETARGET
+ "TotalUses", // TOTALUSES
+ "WearoutTarget", // WEAROUTTARGET
+ "WeaponType", // WEAPONTYPE
+ "WeaponAttackValue", // WEAPONATTACKVALUE
+ "WeaponDefendValue", // WEAPONDEFENDVALUE
+ "ShieldDefendValue", // SHIELDDEFENDVALUE
+ "BowRange", // BOWRANGE
+ "BowAmmoType", // BOWAMMOTYPE
+ "ThrowRange", // THROWRANGE
+ "ThrowAttackValue", // THROWATTACKVALUE
+ "ThrowDefendValue", // THROWDEFENDVALUE
+ "ThrowMissile", // THROWMISSILE
+ "ThrowSpecialEffect", // THROWSPECIALEFFECT
+ "ThrowEffectStrength", // THROWEFFECTSTRENGTH
+ "ThrowFragility", // THROWFRAGILITY
+ "WandRange", // WANDRANGE
+ "WandManaConsumption", // WANDMANACONSUMPTION
+ "WandAttackStrength", // WANDATTACKSTRENGTH
+ "WandAttackVariation", // WANDATTACKVARIATION
+ "WandDamageType", // WANDDAMAGETYPE
+ "WandMissile", // WANDMISSILE
+ "AmmoType", // AMMOTYPE
+ "AmmoAttackValue", // AMMOATTACKVALUE
+ "AmmoMissile", // AMMOMISSILE
+ "AmmoSpecialEffect", // AMMOSPECIALEFFECT
+ "AmmoEffectStrength", // AMMOEFFECTSTRENGTH
+ "ArmorValue", // ARMORVALUE
+ "Elevation", // ELEVATION
+ "DisguiseTarget", // DISGUISETARGET
+ "Meaning", // MEANING
+};
+
static const char InstanceAttributeNames[18][30] = {
"Content", // CONTENT
"ChestQuestNumber", // CHESTQUESTNUMBER
@@ -111,10 +254,32 @@ static const char InstanceAttributeNames[18][30] = {
"RemainingUses", // REMAININGUSES
};
+//
+
+// BITSET Helpers
+// =============================================================================
+static inline bool CheckBit(uint8 *Set, int Index){
+ int ByteIndex = (int)(Index / 8);
+ uint8 BitMask = (uint8)(1 << (Index % 8));
+ return (Set[ByteIndex] & BitMask) != 0;
+}
+
+static inline void SetBit(uint8 *Set, int Index){
+ int ByteIndex = (int)(Index / 8);
+ uint8 BitMask = (uint8)(1 << (Index % 8));
+ Set[ByteIndex] |= BitMask;
+}
+
+static inline void ClearBit(uint8 *Set, int Index){
+ int ByteIndex = (int)(Index / 8);
+ uint8 BitMask = (uint8)(1 << (Index % 8));
+ Set[ByteIndex] &= ~BitMask;
+}
+
// ObjectType
// =============================================================================
void ObjectType::setTypeID(int TypeID){
- if(TypeID < ObjectTypes.min || ObjectTypes.max < TypeID){
+ if(!ObjectTypeExists(TypeID)){
error("ObjectType::setTypeID: Ungültiger Typ %d.\n", TypeID);
TypeID = 0;
}
@@ -123,10 +288,8 @@ void ObjectType::setTypeID(int TypeID){
}
bool ObjectType::getFlag(FLAG Flag){
- TObjectType *Type = ObjectTypes.at(this->TypeID);
- int FlagIndex = (int)(Flag / 8);
- uint8 FlagMask = (uint8)(1 << (Flag % 8));
- return (Type->Flags[FlagIndex] & FlagMask) != 0;
+ TObjectType *TypeP = ObjectTypes.at(this->TypeID);
+ return CheckBit(TypeP->Flags, (int)Flag);
}
uint32 ObjectType::getAttribute(TYPEATTRIBUTE Attribute){
@@ -136,23 +299,24 @@ uint32 ObjectType::getAttribute(TYPEATTRIBUTE Attribute){
return 0;
}
- TObjectType *Type = ObjectTypes.at(this->TypeID);
- return Type->Attributes[Attribute];
+ TObjectType *TypeP = ObjectTypes.at(this->TypeID);
+ return TypeP->Attributes[Attribute];
}
int ObjectType::getAttributeOffset(INSTANCEATTRIBUTE Attribute){
if(!this->getFlag(InstanceAttributeFlags[Attribute])){
+ // TODO(fusion): Review this?
if(Attribute != CONTENT || !this->getFlag(CHEST)){
return -1;
}
}
- TObjectType *Type = ObjectTypes.at(this->TypeID);
- return Type->AttributeOffsets[Attribute];
+ TObjectType *TypeP = ObjectTypes.at(this->TypeID);
+ return TypeP->AttributeOffsets[Attribute];
}
const char *ObjectType::getName(int Count){
- if(this->TypeID == 99){
+ if(this->isCreatureContainer()){
error("ObjectType::getName: Der Kreaturtyp hat keinen Namen.\n");
return NULL;
}
@@ -162,9 +326,9 @@ const char *ObjectType::getName(int Count){
// buffer.
#if 0
char ObjectName[50];
- TObjectType *Type = ObjectTypes.at(this->TypeID);
+ TObjectType *TypeP = ObjectTypes.at(this->TypeID);
if(Type->Name != NULL){
- strcpy(ObjectName, Type->Name);
+ strcpy(ObjectName, TypeP->Name);
}else{
ObjectName[0] = 0;
}
@@ -175,15 +339,32 @@ const char *ObjectType::getName(int Count){
}
const char *ObjectType::getDescription(void){
- TObjectType *Type = ObjectTypes.at(this->TypeID);
- return Type->Description;
+ TObjectType *TypeP = ObjectTypes.at(this->TypeID);
+ return TypeP->Description;
}
// Object Type Related Functions
// =============================================================================
-const char *GetInstanceAttributeName(int Attribute){
- ASSERT(Attribute >= 0 && Attribute <= 17);
- return InstanceAttributeNames[Attribute];
+int GetFlagByName(const char *Name){
+ int Result = -1;
+ for(int i = 0; i < NARRAY(FlagNames); i += 1){
+ if(stricmp(FlagNames[i], Name) == 0){
+ Result = i;
+ break;
+ }
+ }
+ return Result;
+}
+
+int GetTypeAttributeByName(const char *Name){
+ int Result = -1;
+ for(int i = 0; i < NARRAY(TypeAttributeNames); i += 1){
+ if(stricmp(TypeAttributeNames[i], Name) == 0){
+ Result = i;
+ break;
+ }
+ }
+ return Result;
}
int GetInstanceAttributeByName(const char *Name){
@@ -197,6 +378,278 @@ int GetInstanceAttributeByName(const char *Name){
return Result;
}
+const char *GetFlagName(int Flag){
+ ASSERT(Flag >= 0 && Flag < NARRAY(FlagNames));
+ return FlagNames[Flag];
+}
+
+const char *GetTypeAttributeName(int Attribute){
+ ASSERT(Attribute >= 0 && Attribute < NARRAY(TypeAttributeFlags));
+ return TypeAttributeNames[Attribute];
+}
+
+const char *GetInstanceAttributeName(int Attribute){
+ ASSERT(Attribute >= 0 && Attribute < NARRAY(InstanceAttributeNames));
+ return InstanceAttributeNames[Attribute];
+}
+
bool ObjectTypeExists(int TypeID){
return ObjectTypes.min <= TypeID && TypeID <= ObjectTypes.max;
}
+
+// NOTE(fusion): Small helper to compute the index into the `NewType` array
+// from old object type values.
+static uint16 GetNewTypeIndex(uint8 Group, uint8 Number){
+ return ((uint16)Group << 8) | (uint16)Number;
+}
+
+bool ObjectTypeExists(uint8 Group, uint8 Number){
+ // TODO(fusion): Check bounds?
+ return NewType[GetNewTypeIndex(Group, Number)] >= 0;
+}
+
+ObjectType GetNewObjectType(uint8 Group, uint8 Number){
+ int TypeID = NewType[GetNewTypeIndex(Group, Number)];
+ if(TypeID < 0){
+ error("GetNewObjectType: Objekttyp %u/%u existiert nicht.\n", Group, Number);
+ TypeID = 0;
+ }
+
+ return ObjectType(TypeID);
+}
+
+void GetOldObjectType(ObjectType Type, uint8 *Group, uint8 *Number){
+ if(!ObjectTypeExists(Type.TypeID)){
+ error("GetOldObjectType: Ungültiger Typ %d.\n", Type.TypeID);
+ return;
+ }
+
+ // TODO(fusion): Check bounds?
+ *Group = OldGroup[Type.TypeID];
+ *Number = OldNumber[Type.TypeID];
+}
+
+ObjectType GetSpecialObject(SPECIALMEANING Meaning){
+ int TypeID = 0;
+ if(Meaning > 0 && Meaning < NARRAY(SpecialObjects)){
+ TypeID = SpecialObjects[Meaning].TypeID;
+ if(TypeID == 0){
+ error("GetSpecialObject: Kein Objekttyp mit Bedeutung %d definiert.\n", Meaning);
+ }
+ }else{
+ error("GetSpecialObject: Ungültige Bedeutung %d.\n", Meaning);
+ }
+ return ObjectType(TypeID);
+}
+
+ObjectType GetObjectTypeByName(const char *SearchName, bool Movable){
+ if(SearchName == NULL){
+ error("GetObjectTypeByName: SearchName ist NULL.\n");
+ return ObjectType();
+ }
+
+ // NOTE(fusion): We add spaces around the search term to match whole words
+ // or sentences. This requires use to also add spaces around the searching
+ // text, in case the search term is at edge.
+ ObjectType BestMatch;
+ char Help[50];
+ char Pattern[50];
+ snprintf(Pattern, sizeof(Pattern), " %s ", SearchName);
+ for(int TypeID = TYPEID_CREATURE_CONTAINER + 1;
+ TypeID <= ObjectTypes.max;
+ TypeID += 1){
+ ObjectType Type = TypeID;
+ if(!Movable || !Type.getFlag(UNMOVE)){
+ const char *TypeName = Type.getName(-1);
+ if(TypeName && TypeName[0] != 0){
+ if(stricmp(SearchName, TypeName) == 0){
+ BestMatch = Type;
+ break;
+ }
+
+ if(BestMatch.TypeID == 0){
+ snprintf(Help, sizeof(Help), " %s ", TypeName);
+ if(strstr(Help, Pattern) != NULL){
+ BestMatch = Type;
+ }
+ }
+ }
+ }
+ }
+
+ return BestMatch;
+}
+
+static void LoadObjects(void){
+ char FileName[4096];
+ snprintf(FileName, sizeof(FileName), "%s/objects.srv", DATAPATH);
+
+ int TypeID = -1;
+ TReadScriptFile Script;
+ Script.open(FileName);
+ while(true){
+ Script.nextToken();
+ if(Script.Token == ENDOFFILE){
+ Script.close();
+ break;
+ }
+
+ if(Script.Token != IDENTIFIER){
+ Script.error("Identifier expected");
+ }
+
+ char Identifier[MAX_IDENT_LENGTH];
+ strcpy(Identifier, Script.getIdentifier());
+ Script.readSymbol('=');
+
+ if(strcmp(Identifier, "typeid") == 0){
+ TypeID = Script.readNumber();
+ // NOTE(fusion): Initialize object type.
+ TObjectType *TypeP = ObjectTypes.at(TypeID);
+ memset(TypeP, 0, sizeof(TObjectType));
+ for(int i = 0; i < NARRAY(TypeP->AttributeOffsets); i += 1){
+ TypeP->AttributeOffsets[i] = -1;
+ }
+ }else{
+ if(TypeID < 0){
+ Script.error("Invalid type id");
+ }
+
+ TObjectType *TypeP = ObjectTypes.at(TypeID);
+ if(strcmp(Identifier, "name") == 0){
+ TypeP->Name = AddStaticString(Script.readString());
+ }else if(strcmp(Identifier, "description") == 0){
+ TypeP->Description = AddStaticString(Script.readString());
+ }else if(strcmp(Identifier, "flags") == 0){
+ Script.readSymbol('{');
+ while(true){
+ Script.nextToken();
+ if(Script.Token == SPECIAL){
+ char Special = Script.getSpecial();
+ if(Special == ',') continue;
+ if(Special == '}') break;
+ }
+
+ int Flag = GetFlagByName(Script.getIdentifier());
+ if(Flag == -1){
+ Script.error("Unknown flag");
+ }
+
+ SetBit(TypeP->Flags, Flag);
+ }
+
+ // NOTE(fusion): Assign instance attribute offsets after parsing flags.
+ int InstanceAttributeCount = 0;
+ for(int InstanceAttribute = 0;
+ InstanceAttribute < NARRAY(TypeP->AttributeOffsets);
+ InstanceAttribute += 1){
+ // TODO(fusion): This thing with CONTENT, CONTAINER, and CHEST have
+ // been a trend so my guess it that CONTENT is the only instance
+ // attribute that has two flags (CONTAINER and CHEST), and we need
+ // to check it here.
+ if(CheckBit(TypeP->Flags, InstanceAttributeFlags[InstanceAttribute])
+ || (InstanceAttribute == CONTENT && CheckBit(TypeP->Flags, CHEST))){
+ TypeP->AttributeOffsets[InstanceAttribute] = InstanceAttributeCount;
+ InstanceAttributeCount += 1;
+ }
+ }
+ }else if(strcmp(Identifier, "attributes") == 0){
+ Script.readSymbol('{');
+ while(true){
+ Script.nextToken();
+ if(Script.Token == SPECIAL){
+ char Special = Script.getSpecial();
+ if(Special == ',') continue;
+ if(Special == '}') break;
+ }
+
+ int TypeAttribute = GetTypeAttributeByName(Script.getIdentifier());
+ if(TypeAttribute == -1){
+ Script.error("Unknown attribute");
+ }
+
+ Script.readSymbol('=');
+ uint32 Value = (uint32)Script.readNumber();
+ TypeP->Attributes[TypeAttribute] = Value;
+ if(TypeAttribute == MEANING){
+ if(Value == 0 || Value >= NARRAY(SpecialObjects)){
+ Script.error("Unknown meaning");
+ }
+
+ if(SpecialObjects[Value].TypeID != 0){
+ Script.error("Special object already defined");
+ }
+
+ SpecialObjects[Value].setTypeID(TypeID);
+ }
+ }
+ }else{
+ // TODO(fusion):
+ // error("Invalid object type field \"%s\"", Identifier);
+ }
+ }
+ }
+}
+
+static void LoadConversionList(void){
+ STATIC_ASSERT(NARRAY(OldGroup) == NARRAY(OldNumber));
+
+ for(int i = 0; i < NARRAY(OldGroup); i += 1){
+ OldGroup[i] = 0;
+ OldNumber[i] = 0;
+ }
+
+ for(int i = 0; i < NARRAY(NewType); i += 1){
+ NewType[i] = -1;
+ }
+
+ char FileName[4096];
+ snprintf(FileName, sizeof(FileName), "%s/conversion.lst", DATAPATH);
+ std::ifstream IN(FileName, std::ios_base::in);
+ if(IN.fail()){
+ error("LoadConversionList: Kann Datei %s nicht öffnen.\n", FileName);
+ throw "Cannot open conversion.lst";
+ }
+
+ while(true){
+ int Group, Number, TypeID;
+ IN >> Group >> Number >> TypeID;
+ if(IN.fail()){
+ // TODO(fusion): if(!IN.eof()) { error(...); }
+ IN.close();
+ break;
+ }
+
+ // TODO(fusion): Review this?
+ if(TypeID <= 0 || TypeID >= NARRAY(OldGroup)){
+ error("LoadConversionList: Ungültiger Typ %d.\n", TypeID);
+ throw "Error while loading conversion.lst";
+ }
+
+ OldGroup[TypeID] = Group;
+ OldNumber[TypeID] = Number;
+ NewType[GetNewTypeIndex(Group, Number)] = TypeID;
+ }
+
+
+ // NOTE(fusion): Map and body containers.
+ for(int TypeID = 0; TypeID <= 10; TypeID += 1){
+ OldGroup[TypeID] = 0;
+ OldNumber[TypeID] = (uint8)TypeID;
+ NewType[GetNewTypeIndex(0, (uint8)TypeID)] = TypeID;
+ }
+
+ // NOTE(fusion): Creature container.
+ OldGroup[99] = 250;
+ OldNumber[00] = 0;
+ NewType[GetNewTypeIndex(250, 0)] = 99;
+}
+
+void InitObjects(void){
+ LoadObjects();
+ LoadConversionList();
+}
+
+void ExitObjects(void){
+ // no-op
+}
diff --git a/src/objects.hh b/src/objects.hh
index eec0565..84801e3 100644
--- a/src/objects.hh
+++ b/src/objects.hh
@@ -66,17 +66,26 @@ struct ObjectType {
};
struct TObjectType {
- char *Name;
- char *Description;
+ const char *Name;
+ const char *Description;
uint8 Flags[9];
uint32 Attributes[62];
int AttributeOffsets[18];
};
-const char *GetInstanceAttributeName(int Attribute);
+int GetFlagByName(const char *Name);
+int GetTypeAttributeByName(const char *Name);
int GetInstanceAttributeByName(const char *Name);
+const char *GetFlagName(int Flag);
+const char *GetTypeAttributeName(int Attribute);
+const char *GetInstanceAttributeName(int Attribute);
bool ObjectTypeExists(int TypeID);
-//void InitObjects(void);
-//void ExitObjects(void);
+bool ObjectTypeExists(uint8 Group, uint8 Number);
+ObjectType GetNewObjectType(uint8 Group, uint8 Number);
+void GetOldObjectType(ObjectType Type, uint8 *Group, uint8 *Number);
+ObjectType GetSpecialObject(SPECIALMEANING Meaning);
+ObjectType GetObjectTypeByName(const char *SearchName, bool Movable);
+void InitObjects(void);
+void ExitObjects(void);
#endif //TIBIA_OBJECTS_HH_
diff --git a/src/stubs.hh b/src/stubs.hh
index d0b7d90..316a2fe 100644
--- a/src/stubs.hh
+++ b/src/stubs.hh
@@ -15,7 +15,8 @@ typedef void TRefreshSectorFunction(int SectorX, int SectorY, int SectorZ, const
typedef void TSendMailsFunction(TPlayerData *PlayerData);
extern void AbortWriter(void);
-extern uint32 AddDynamicString(const char *Text);
+extern uint32 AddDynamicString(const char *String);
+extern const char *AddStaticString(const char *String);
extern void AnnounceChangedCreature(uint32 CreatureID, int Type);
extern void BroadcastMessage(int Mode, const char *Text, ...) ATTR_PRINTF(2, 3);
extern void Change(Object Obj, ObjectType NewType, uint32 Value);