diff options
| author | fusion32 <marcopuzziello@gmail.com> | 2025-05-28 00:39:29 -0300 |
|---|---|---|
| committer | fusion32 <marcopuzziello@gmail.com> | 2025-05-28 00:39:29 -0300 |
| commit | 5505f28c358a50e9d610784303485cc757459162 (patch) | |
| tree | 7756da65dde0d3c125ae71f2ce2afdeb7dc8cd54 /src/objects.cc | |
| parent | 42959207914208438261810757abea43422a28fe (diff) | |
| download | game-5505f28c358a50e9d610784303485cc757459162.tar.gz game-5505f28c358a50e9d610784303485cc757459162.zip | |
finish objects.cc
Diffstat (limited to 'src/objects.cc')
| -rw-r--r-- | src/objects.cc | 487 |
1 files changed, 470 insertions, 17 deletions
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 +} |
