aboutsummaryrefslogtreecommitdiff
path: root/src/map.cc
diff options
context:
space:
mode:
authorfusion32 <marcopuzziello@gmail.com>2025-05-26 01:44:13 -0300
committerfusion32 <marcopuzziello@gmail.com>2025-05-26 01:44:13 -0300
commitfbb392953c5af29b6a0924590c24a05befb72e56 (patch)
treed999d2cdb92824f1dbbb93111164696fba607328 /src/map.cc
parentc2f41059c71a0c7bfc0b64a7f53334d3bfd0ee76 (diff)
downloadgame-fbb392953c5af29b6a0924590c24a05befb72e56.tar.gz
game-fbb392953c5af29b6a0924590c24a05befb72e56.zip
more object functions
Diffstat (limited to 'src/map.cc')
-rw-r--r--src/map.cc394
1 files changed, 316 insertions, 78 deletions
diff --git a/src/map.cc b/src/map.cc
index 20dae79..0930083 100644
--- a/src/map.cc
+++ b/src/map.cc
@@ -8,30 +8,6 @@
#include <dirent.h>
-// NOTE(fusion): This is used by hash table entries and sectors to tell whether
-// they're currently loaded or swapped out to disk.
-enum : uint8 {
- STATUS_FREE = 0,
- STATUS_LOADED = 1,
- STATUS_SWAPPED = 2,
-
- // TODO(fusion): It seems this is only used with the `NONE` entry in the
- // hash table. I haven't seen it used **yet** but It may have a purpose
- // aside from preventing swap outs.
- STATUS_PERMANENT = 255,
-};
-
-// NOTE(fusion): This is used to determine precedence order of different objects
-// in a tile.
-enum : int {
- PRIORITY_BANK = 0,
- PRIORITY_CLIP = 1,
- PRIORITY_BOTTOM = 2,
- PRIORITY_TOP = 3,
- PRIORITY_CREATURE = 4,
- PRIORITY_OTHER = 5,
-};
-
static int OBCount;
static int SectorXMin;
static int SectorXMax;
@@ -1207,6 +1183,24 @@ void ExitMap(bool Save){
// Object Functions
// =============================================================================
+TObject *AccessObject(Object Obj){
+ if(Obj == NONE){
+ error("AccessObject: Ungültige Objektnummer Null.\n");
+ return HashTableData[0];
+ }
+
+ uint32 EntryIndex = Obj.ObjectID & HashTableMask;
+ if(HashTableType[EntryIndex] == STATUS_SWAPPED){
+ UnswapSector((uint32)((uintptr)HashTableData[EntryIndex]));
+ }
+
+ if(HashTableType[EntryIndex] == STATUS_LOADED && HashTableData[EntryIndex]->ObjectID == Obj.ObjectID){
+ return HashTableData[EntryIndex];
+ }else{
+ return HashTableData[0];
+ }
+}
+
Object CreateObject(void){
static uint32 NextObjectID = 1;
@@ -1240,25 +1234,58 @@ Object CreateObject(void){
return Object(NextObjectID);
}
-// DestroyObject
-// DeleteObject
+void DestroyObject(Object Obj){
+ if(!Obj.exists()){
+ error("DestroyObject: Übergebenes Objekt existiert nicht.\n");
+ return;
+ }
+
+ ObjectType ObjType = Obj.getObjectType();
+ if(ObjType.getFlag(TEXT)){
+ DeleteDynamicString(Obj.getAttribute(TEXTSTRING));
+ DeleteDynamicString(Obj.getAttribute(EDITOR));
+ }
-TObject *AccessObject(Object Obj){
+ if(ObjType.getFlag(CONTAINER) || ObjType.getFlag(CHEST)){
+ while(true){
+ Object Inner = Obj.getAttribute(CONTENT);
+ if(Inner == NONE){
+ break;
+ }
+ DeleteObject(Inner);
+ }
+ }
+
+ if(ObjType.getFlag(EXPIRE)){
+ CronStop(Obj);
+ }
+
+ // TODO(fusion): I feel this should be checked up front?
if(Obj == NONE){
- error("AccessObject: Ungültige Objektnummer Null.\n");
- return HashTableData[0];
+ error("DestroyObject: Ungültige Objektnummer %d.\n", NONE.ObjectID);
+ return;
}
uint32 EntryIndex = Obj.ObjectID & HashTableMask;
- if(HashTableType[EntryIndex] == STATUS_SWAPPED){
- UnswapSector((uint32)((uintptr)HashTableData[EntryIndex]));
+ if(HashTableType[EntryIndex] != STATUS_LOADED){
+ error("DestroyObject: Objekt steht nicht im Speicher.\n");
+ return;
}
- if(HashTableType[EntryIndex] == STATUS_LOADED && HashTableData[EntryIndex]->ObjectID == Obj.ObjectID){
- return HashTableData[EntryIndex];
- }else{
- return HashTableData[0];
+ PutFreeObjectSlot(HashTableData[EntryIndex]);
+ HashTableType[EntryIndex] = STATUS_FREE;
+ HashTableFree += 1;
+ DecrementObjectCounter();
+}
+
+void DeleteObject(Object Obj){
+ if(!Obj.exists()){
+ error("DeleteObject: Übergebenes Objekt existiert nicht.\n");
+ return;
}
+
+ CutObject(Obj);
+ DestroyObject(Obj);
}
void ChangeObject(Object Obj, ObjectType NewType){
@@ -1338,6 +1365,41 @@ void ChangeObject(Object Obj, ObjectType NewType){
CronExpire(Obj, Delay);
}
+void ChangeObject(Object Obj, INSTANCEATTRIBUTE Attribute, uint32 Value){
+ if(!Obj.exists()){
+ error("ChangeObject: Übergebenes Objekt existiert nicht.\n");
+ return;
+ }
+
+ Obj.setAttribute(Attribute, Value);
+}
+
+void ChangeObject(Object Obj, ObjectType NewType, uint32 Value){
+ if(!Obj.exists()){
+ error("ChangeObject: Übergebenes Objekt existiert nicht (1, NewType=%d).\n", NewType.TypeID);
+ return;
+ }
+
+ // TODO(fusion): Why are we checking if `Obj` exists a second time? There is
+ // no reason to assume `Obj.getContainer()` would change anything since the
+ // first call.
+ Object Con = Obj.getContainer();
+ if(!Obj.exists()){
+ error("ChangeObject: Übergebenes Objekt existiert nicht (2, NewType=%d).\n", NewType.TypeID);
+ return;
+ }
+
+ ObjectType ObjType = Obj.getObjectType();
+ if(ObjType.getFlag(CUMULATIVE)){
+ uint32 Amount = Obj.getAttribute(AMOUNT);
+ if(Amount > 1){
+ Move(0, Obj, Con, Amount - 1, true, NONE);
+ }
+ }
+
+ Change(Obj, NewType, Value);
+}
+
int GetObjectPriority(Object Obj){
if(!Obj.exists()){
error("GetObjectPriority: Übergebenes Objekt existiert nicht.\n");
@@ -1439,6 +1501,46 @@ void PlaceObject(Object Obj, Object Con, bool Append){
Obj.setContainer(Con);
}
+// NOTE(fusion): This is the opposite of `PlaceObject`.
+void CutObject(Object Obj){
+ if(!Obj.exists()){
+ error("CutObject: Übergebenes Objekt existiert nicht.\n");
+ return;
+ }
+
+ Object Con = Obj.getContainer();
+ Object Cur = GetFirstContainerObject(Con);
+ if(Cur == Obj){
+ Object Next = Obj.getNextObject();
+ Con.setAttribute(CONTENT, Next.ObjectID);
+ }else{
+ Object Prev;
+ do{
+ Prev = Cur;
+ Cur = Cur.getNextObject();
+ }while(Cur != Obj);
+ Prev.setNextObject(Cur.getNextObject());
+ }
+
+ Obj.setNextObject(NONE);
+ Obj.setContainer(NONE);
+}
+
+void MoveObject(Object Obj, Object Con){
+ if(!Obj.exists()){
+ error("MoveObject: Übergebenes Objekt existiert nicht.\n");
+ return;
+ }
+
+ if(!Con.exists()){
+ error("MoveObject: Übergebener Zielcontainer existiert nicht.\n");
+ return;
+ }
+
+ CutObject(Obj);
+ PlaceObject(Obj, Con, false);
+}
+
Object AppendObject(Object Con, ObjectType Type){
if(!Con.exists()){
error("AppendObject: Übergebener Container existiert nicht.\n");
@@ -1451,6 +1553,143 @@ Object AppendObject(Object Con, ObjectType Type){
return Obj;
}
+Object SetObject(Object Con, ObjectType Type, uint32 CreatureID){
+ if(!Con.exists()){
+ error("SetObject: Übergebener Container existiert nicht.\n");
+ return NONE;
+ }
+
+ Object Obj = CreateObject();
+ ChangeObject(Obj, Type);
+ PlaceObject(Obj, Con, false);
+ if(Type.isCreatureContainer()){
+ if(CreatureID == 0){
+ error("SetObject: Ungültige Kreatur-ID.\n");
+ }
+ AccessObject(Obj)->Attributes[1] = CreatureID;
+ }
+ return Obj;
+}
+
+Object CopyObject(Object Con, Object Source){
+ if(!Source.exists()){
+ error("CopyObject: Vorlage existiert nicht.\n");
+ return NONE;
+ }
+
+ if(!Con.exists()){
+ error("CopyObject: Zielcontainer existiert nicht.\n");
+ return NONE;
+ }
+
+ ObjectType SourceType = Source.getObjectType();
+ if(SourceType.isCreatureContainer()){
+ error("CopyObject: Kreaturen dürfen nicht kopiert werden.\n");
+ return NONE;
+ }
+
+ Object NewObj = SetObject(Con, SourceType, 0);
+ for(int i = 0; i < NARRAY(TObject::Attributes); i += 1){
+ AccessObject(NewObj)->Attributes[i] = AccessObject(Source)->Attributes[i];
+ }
+
+ if(SourceType.getFlag(CONTAINER) || SourceType.getFlag(CHEST)){
+ NewObj.setAttribute(CONTENT, NONE.ObjectID);
+ }
+
+ if(SourceType.getFlag(TEXT)){
+ // NOTE(fusion): Both `NewObj` and `Source` share the same strings. We
+ // need to duplicate them so both objects can "own" and manage their own
+ // strings separately.
+ uint32 TextString = NewObj.getAttribute(TEXTSTRING);
+ if(TextString != 0){
+ TextString = AddDynamicString(GetDynamicString(TextString));
+ NewObj.setAttribute(TEXTSTRING, TextString);
+ }
+
+ uint32 Editor = NewObj.getAttribute(EDITOR);
+ if(Editor != 0){
+ Editor = AddDynamicString(GetDynamicString(TextString));
+ NewObj.setAttribute(EDITOR, Editor);
+ }
+ }
+
+ return NewObj;
+}
+
+Object SplitObject(Object Obj, int Count){
+ if(!Obj.exists()){
+ error("SplitObject: Übergebenes Objekt existiert nicht.\n");
+ return NONE;
+ }
+
+ ObjectType ObjType = Obj.getObjectType();
+ if(!ObjType.getFlag(CUMULATIVE)){
+ error("SplitObject: Objekt ist nicht kumulierbar.\n");
+ return Obj; // TODO(fusion): Probably return NONE?
+ }
+
+ uint32 Amount = Obj.getAttribute(AMOUNT);
+ if(Count <= 0 || (uint32)Count > Amount){
+ error("SplitObject: Ungültiger Zähler %d.\n", Count);
+ return Obj; // TODO(fusion): Probably return NONE?
+ }
+
+ Object Res = Obj;
+ if((uint32)Count != Amount){
+ Res = CopyObject(Obj.getContainer(), Obj);
+ Res.setAttribute(AMOUNT, (uint32)Count);
+ Obj.setAttribute(AMOUNT, Amount - (uint32)Count);
+ }
+ return Res;
+}
+
+void MergeObjects(Object Obj, Object Dest){
+ if(!Obj.exists()){
+ error("MergeObjects: Übergebenes Objekt existiert nicht.\n");
+ return;
+ }
+
+ if(!Dest.exists()){
+ error("MergeObjects: Übergebenes Ziel existiert nicht.\n");
+ return;
+ }
+
+ ObjectType ObjType = Obj.getObjectType();
+ ObjectType DestType = Dest.getObjectType();
+ if(ObjType != DestType){
+ error("MergeObjects: Objekttypen %d und %d sind nicht identisch.\n",
+ ObjType.TypeID, DestType.TypeID);
+ return;
+ }
+
+ if(!ObjType.getFlag(CUMULATIVE)){
+ error("MergeObjects: Objekttyp %d ist nicht kumulierbar.\n", ObjType.TypeID);
+ return;
+ }
+
+ uint32 ObjAmount = Obj.getAttribute(AMOUNT);
+ if(ObjAmount == 0){
+ error("MergeObjects: Objekt enthält 0 Teile.\n");
+ ObjAmount = 1;
+ }
+
+ uint32 DestAmount = Dest.getAttribute(AMOUNT);
+ if(DestAmount == 0){
+ error("MergeObjects: Zielobjekt enthält 0 Teile.\n");
+ DestAmount = 1;
+ }
+
+ DestAmount += ObjAmount;
+ if(DestAmount > 100){
+ error("MergeObjects: Neues Objekt enthält mehr als 100 Teile.\n");
+ DestAmount = 100;
+ }
+
+ Dest.setAttribute(AMOUNT, DestAmount);
+ DeleteObject(Obj);
+}
+
Object GetFirstContainerObject(Object Con){
if(Con == NONE){
error("GetFirstContainerObject: Übergebener Container existiert nicht.\n");
@@ -1478,53 +1717,14 @@ Object GetContainerObject(Object Con, int Index){
}
Object Obj = GetFirstContainerObject(Con);
- while(Obj != NONE && Index > 0){
+ while(Index > 0 && Obj != NONE){
+ Index -= 1;
Obj = Obj.getNextObject();
}
return Obj;
}
-void GetObjectCoordinates(Object Obj, int *x, int *y, int *z){
- if(!Obj.exists()){
- error("GetObjectCoordinates: Übergebenes Objekt existiert nicht.\n");
- *x = 0;
- *y = 0;
- *z = 0;
- return;
- }
-
- // TODO(fusion): I'm not sure I like this approach with calling `AccessObject`
- // multiple times for the same object. Furthermore, using `Object::getObjectType`
- // (at least until now) is very weird when we could just get the same information
- // from the `TObject` which we'll access ANYWAYS.
-
- while(true){
- if(Obj.getObjectType().isMapContainer())
- break;
- Obj = Obj.getContainer();
- }
-
- *x = AccessObject(Obj)->Attributes[1];
- *y = AccessObject(Obj)->Attributes[2];
-
- // NOTE(fusion): The first 8 bits of `Attributes[3]` holds the Z coordinate
- // of a map container. The next 8 bits holds the flags of a map container.
- *z = AccessObject(Obj)->Attributes[3] & 0xFF;
-
- return;
-}
-
-uint8 GetMapContainerFlags(Object Obj){
- if(!Obj.exists() || !Obj.getObjectType().isMapContainer()){
- error("GetMapContainerFlags: Objekt ist kein MapContainer.\n");
- return 0;
- }
-
- // NOTE(fusion): See note in `GetObjectCoordinates` just above.
- return (uint8)(AccessObject(Obj)->Attributes[3] >> 8);
-}
-
Object GetMapContainer(int x, int y, int z){
int SectorX = x / 32;
int SectorY = y / 32;
@@ -1542,7 +1742,6 @@ Object GetMapContainer(int x, int y, int z){
return NONE;
}
-
int OffsetX = x % 32;
int OffsetY = y % 32;
ConSector->TimeStamp = RoundNr;
@@ -1555,7 +1754,7 @@ Object GetMapContainer(Object Obj){
return NONE;
}
- while(true){
+ while(Obj != NONE){
if(Obj.getObjectType().isMapContainer())
break;
Obj = Obj.getContainer();
@@ -1572,3 +1771,42 @@ Object GetFirstObject(int x, int y, int z){
return NONE;
}
}
+
+uint8 GetMapContainerFlags(Object Obj){
+ if(!Obj.exists() || !Obj.getObjectType().isMapContainer()){
+ error("GetMapContainerFlags: Objekt ist kein MapContainer.\n");
+ return 0;
+ }
+
+ // NOTE(fusion): See note in `GetObjectCoordinates`.
+ return (uint8)(AccessObject(Obj)->Attributes[3] >> 8);
+}
+
+void GetObjectCoordinates(Object Obj, int *x, int *y, int *z){
+ if(!Obj.exists()){
+ error("GetObjectCoordinates: Übergebenes Objekt existiert nicht.\n");
+ *x = 0;
+ *y = 0;
+ *z = 0;
+ return;
+ }
+
+ // TODO(fusion): I'm not sure I like this approach with calling `AccessObject`
+ // multiple times for the same object. Furthermore, using `Object::getObjectType`
+ // (at least until now) is very weird when we could just get the same information
+ // from the `TObject` which we'll access ANYWAYS.
+
+ while(true){
+ if(Obj.getObjectType().isMapContainer())
+ break;
+ Obj = Obj.getContainer();
+ }
+
+ *x = AccessObject(Obj)->Attributes[1];
+ *y = AccessObject(Obj)->Attributes[2];
+
+ // NOTE(fusion): The first 8 bits of `Attributes[3]` holds the Z coordinate
+ // of a map container. The next 8 bits holds its flags and the last 16 bits
+ // holds its house id.
+ *z = AccessObject(Obj)->Attributes[3] & 0xFF;
+}