some work on crmain.cc and cract.cc + merge creature headers
This commit is contained in:
parent
850fa1c0e1
commit
76f6ecb7c8
6
Makefile
6
Makefile
@ -15,13 +15,17 @@ endif
|
||||
|
||||
HEADERS = $(SRCDIR)/common.hh $(SRCDIR)/config.hh $(SRCDIR)/connection.hh $(SRCDIR)/containers.hh $(SRCDIR)/cr.hh $(SRCDIR)/enums.hh $(SRCDIR)/info.hh $(SRCDIR)/magic.hh $(SRCDIR)/map.hh $(SRCDIR)/objects.hh $(SRCDIR)/script.hh $(SRCDIR)/stubs.hh $(SRCDIR)/thread.hh
|
||||
|
||||
$(BUILDDIR)/$(OUTPUTEXE): $(BUILDDIR)/config.obj $(BUILDDIR)/crcombat.obj $(BUILDDIR)/crmain.obj $(BUILDDIR)/crplayer.obj $(BUILDDIR)/crskill.obj $(BUILDDIR)/info.obj $(BUILDDIR)/magic.obj $(BUILDDIR)/main.obj $(BUILDDIR)/map.obj $(BUILDDIR)/objects.obj $(BUILDDIR)/script.obj $(BUILDDIR)/shm.obj $(BUILDDIR)/strings.obj $(BUILDDIR)/thread.obj $(BUILDDIR)/time.obj $(BUILDDIR)/util.obj
|
||||
$(BUILDDIR)/$(OUTPUTEXE): $(BUILDDIR)/config.obj $(BUILDDIR)/cract.obj $(BUILDDIR)/crcombat.obj $(BUILDDIR)/crmain.obj $(BUILDDIR)/crplayer.obj $(BUILDDIR)/crskill.obj $(BUILDDIR)/info.obj $(BUILDDIR)/magic.obj $(BUILDDIR)/main.obj $(BUILDDIR)/map.obj $(BUILDDIR)/objects.obj $(BUILDDIR)/script.obj $(BUILDDIR)/shm.obj $(BUILDDIR)/strings.obj $(BUILDDIR)/thread.obj $(BUILDDIR)/time.obj $(BUILDDIR)/util.obj
|
||||
$(CC) -c $(CFLAGS) $(LFLAGS) -o $@ $^
|
||||
|
||||
$(BUILDDIR)/config.obj: $(SRCDIR)/config.cc $(HEADERS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) -c $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BUILDDIR)/cract.obj: $(SRCDIR)/cract.cc $(HEADERS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) -c $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BUILDDIR)/crcombat.obj: $(SRCDIR)/crcombat.cc $(HEADERS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) -c $(CFLAGS) -o $@ $<
|
||||
|
||||
12
TODO.md
12
TODO.md
@ -1,8 +1,12 @@
|
||||
## TODO NEXT
|
||||
- merge creature headers into a single one CR.HH
|
||||
- this should help preventing dependency cycles and centralize creature data
|
||||
structures and globals in a single place
|
||||
- CRMAIN.CC, CRPLAYER.CC, CRNONPL.CC, CRACT.CC
|
||||
- CRACT.CC
|
||||
- CRMAIN.CC
|
||||
- TCreature::CreatureMoveStimulus
|
||||
- FINDCREATURES
|
||||
- KILL STATS
|
||||
- RACE
|
||||
- RAIDS
|
||||
- CRPLAYER.CC, CRNONPL.CC
|
||||
- HOUSES.CC
|
||||
- INFO.CC
|
||||
- OPERATE.CC
|
||||
|
||||
3443
reference/game.c
3443
reference/game.c
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,9 @@
|
||||
|
||||
struct TXTEASymmetricKey {
|
||||
int (**_vptr.TXTEASymmetricKey)(...); // VTABLE?
|
||||
uint8 m_SymmetricKey[16];
|
||||
uchar m_SymmetricKey[16];
|
||||
};
|
||||
|
||||
|
||||
// NOTE(fusion): Some minimal processing up until here. Then I realized the file would be huge.
|
||||
//==================================================================================================
|
||||
//==================================================================================================
|
||||
//==================================================================================================
|
||||
|
||||
struct TDatabasePoolConnection {
|
||||
TDatabaseConnectionPool *DatabaseConnectionPool;
|
||||
TDatabaseConnection *DatabaseConnection;
|
||||
@ -131,16 +125,6 @@ struct TDelayedMail {
|
||||
int PacketSize;
|
||||
};
|
||||
|
||||
struct TShortwayPoint {
|
||||
int x;
|
||||
int y;
|
||||
int Waypoints;
|
||||
int Waylength;
|
||||
int Heuristic;
|
||||
struct TShortwayPoint *Predecessor;
|
||||
struct TShortwayPoint *NextToExpand;
|
||||
};
|
||||
|
||||
struct TMonsterhome {
|
||||
int Race;
|
||||
int x;
|
||||
@ -153,8 +137,6 @@ struct TMonsterhome {
|
||||
int Timer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct TChannel {
|
||||
ulong Moderator;
|
||||
char ModeratorName[30];
|
||||
@ -394,15 +376,3 @@ struct TRSAPrivateKey {
|
||||
struct vlong m_DP;
|
||||
struct vlong m_DQ;
|
||||
};
|
||||
|
||||
struct TShortway {
|
||||
struct matrix<TShortwayPoint> *Map;
|
||||
struct TShortwayPoint *FirstToExpand;
|
||||
struct TCreature *cr;
|
||||
int VisibleX;
|
||||
int VisibleY;
|
||||
int StartX;
|
||||
int StartY;
|
||||
int StartZ;
|
||||
int MinWaypoints;
|
||||
};
|
||||
|
||||
@ -7,50 +7,54 @@
|
||||
struct TConnection;
|
||||
|
||||
struct TKnownCreature {
|
||||
KNOWNCREATURESTATE State;
|
||||
uint32 CreatureID;
|
||||
TKnownCreature *Next;
|
||||
TConnection *Connection;
|
||||
KNOWNCREATURESTATE State;
|
||||
uint32 CreatureID;
|
||||
TKnownCreature *Next;
|
||||
TConnection *Connection;
|
||||
};
|
||||
|
||||
struct TConnection {
|
||||
uint8 InData[2048];
|
||||
int InDataSize;
|
||||
bool SigIOPending;
|
||||
bool WaitingForACK;
|
||||
uint8 OutData[16384];
|
||||
uint8 field5_0x4806;
|
||||
uint8 field6_0x4807;
|
||||
int NextToSend;
|
||||
int NextToCommit;
|
||||
int NextToWrite;
|
||||
bool Overflow;
|
||||
bool WillingToSend;
|
||||
uint8 field12_0x4816;
|
||||
uint8 field13_0x4817;
|
||||
TConnection *NextSendingConnection;
|
||||
uint32 RandomSeed;
|
||||
void Logout(int Delay, bool StopFight);
|
||||
|
||||
// DATA
|
||||
// =================
|
||||
uint8 InData[2048];
|
||||
int InDataSize;
|
||||
bool SigIOPending;
|
||||
bool WaitingForACK;
|
||||
uint8 OutData[16384];
|
||||
uint8 field5_0x4806;
|
||||
uint8 field6_0x4807;
|
||||
int NextToSend;
|
||||
int NextToCommit;
|
||||
int NextToWrite;
|
||||
bool Overflow;
|
||||
bool WillingToSend;
|
||||
uint8 field12_0x4816;
|
||||
uint8 field13_0x4817;
|
||||
TConnection *NextSendingConnection;
|
||||
uint32 RandomSeed;
|
||||
CONNECTIONSTATE State;
|
||||
pid_t PID;
|
||||
int Socket;
|
||||
char IPAddress[16];
|
||||
// TXTEASymmetricKey SymmetricKey; // TODO
|
||||
bool ConnectionIsOk;
|
||||
bool ClosingIsDelayed;
|
||||
uint8 field23_0x4852;
|
||||
uint8 field24_0x4853;
|
||||
uint32 TimeStamp;
|
||||
uint32 TimeStampAction;
|
||||
int TerminalType;
|
||||
int TerminalVersion;
|
||||
int TerminalOffsetX;
|
||||
int TerminalOffsetY;
|
||||
int TerminalWidth;
|
||||
int TerminalHeight;
|
||||
uint32 CharacterID;
|
||||
char Name[31];
|
||||
uint8 field35_0x4897;
|
||||
TKnownCreature KnownCreatureTable[150];
|
||||
pid_t PID;
|
||||
int Socket;
|
||||
char IPAddress[16];
|
||||
// TXTEASymmetricKey SymmetricKey; // TODO
|
||||
bool ConnectionIsOk;
|
||||
bool ClosingIsDelayed;
|
||||
uint8 field23_0x4852;
|
||||
uint8 field24_0x4853;
|
||||
uint32 TimeStamp;
|
||||
uint32 TimeStampAction;
|
||||
int TerminalType;
|
||||
int TerminalVersion;
|
||||
int TerminalOffsetX;
|
||||
int TerminalOffsetY;
|
||||
int TerminalWidth;
|
||||
int TerminalHeight;
|
||||
uint32 CharacterID;
|
||||
char Name[31];
|
||||
uint8 field35_0x4897;
|
||||
TKnownCreature KnownCreatureTable[150];
|
||||
};
|
||||
|
||||
#endif // TIBIA_CONNECTION_HH_
|
||||
|
||||
@ -137,10 +137,10 @@ struct priority_queue{
|
||||
delete Entry;
|
||||
}
|
||||
|
||||
void insert(K Key, T *Data){
|
||||
void insert(K Key, T Data){
|
||||
this->Entries += 1;
|
||||
int CurrentIndex = this->Entries;
|
||||
*this->Entry->at(CurrentIndex) = {Key, *Data};
|
||||
*this->Entry->at(CurrentIndex) = {Key, Data};
|
||||
while(CurrentIndex > 1){
|
||||
int ParentIndex = CurrentIndex / 2;
|
||||
priority_queue_entry<K, T> *Current = this->Entry->at(CurrentIndex);
|
||||
|
||||
77
src/cr.hh
77
src/cr.hh
@ -421,7 +421,7 @@ struct TToDoEntry {
|
||||
} Turn;
|
||||
|
||||
struct{
|
||||
uint32 Text; // POINTER? Probably a reference from `AddDynamicString`?
|
||||
uint32 Text;
|
||||
int Mode;
|
||||
uint32 Addressee;
|
||||
bool CheckSpamming;
|
||||
@ -434,12 +434,45 @@ struct TToDoEntry {
|
||||
};
|
||||
|
||||
struct TCreature: TSkillBase {
|
||||
// crmain.cc
|
||||
TCreature(void);
|
||||
void Attack(void);
|
||||
int Damage(TCreature *Attacker, int Damage, int DamageType);
|
||||
void SetID(uint32 CharacterID);
|
||||
void DelID(void);
|
||||
void SetInCrList(void);
|
||||
void DelInCrList(void);
|
||||
void StartLogout(bool Force, bool StopFight);
|
||||
int LogoutPossible(void);
|
||||
void BlockLogout(int Delay, bool BlockProtectionZone);
|
||||
void ToDoGo(int DestX, int DestY, int DestZ, bool Dest, int MaxSteps);
|
||||
int GetHealth(void);
|
||||
int GetSpeed(void);
|
||||
int Damage(TCreature *Attacker, int Damage, int DamageType);
|
||||
|
||||
// cract.cc
|
||||
bool SetOnMap(void);
|
||||
bool DelOnMap(void);
|
||||
void Attack(void);
|
||||
void Execute(void);
|
||||
uint32 CalculateDelay(void);
|
||||
bool ToDoClear(void);
|
||||
void ToDoAdd(TToDoEntry TD);
|
||||
void ToDoStop(void);
|
||||
void ToDoStart(void);
|
||||
void ToDoYield(void);
|
||||
void ToDoWait(int Delay);
|
||||
void ToDoGo(int DestX, int DestY, int DestZ, bool MustReach, int MaxSteps);
|
||||
void ToDoRotate(int Direction);
|
||||
void ToDoMove(int ObjX, int ObjY, int ObjZ, ObjectType Type, uint8 RNum,
|
||||
int DestX, int DestY, int DestZ, uint8 Count);
|
||||
void ToDoMove(Object Obj, int DestX, int DestY, int DestZ, uint8 Count);
|
||||
void ToDoTrade(int ObjX, int ObjY, int ObjZ, ObjectType Type, uint8 RNum,
|
||||
uint32 TradePartner);
|
||||
void ToDoUse(uint8 Count, int ObjX1, int ObjY1, int ObjZ1, ObjectType Type1, uint8 RNum1,
|
||||
uint8 Dummy, int ObjX2, int ObjY2, int ObjZ2, ObjectType Type2, uint8 RNum2);
|
||||
void ToDoUse(uint8 Count, Object Obj1, Object Obj2);
|
||||
void ToDoTurn(int ObjX, int ObjY, int ObjZ, ObjectType Type, uint8 RNum);
|
||||
void ToDoAttack(void);
|
||||
void ToDoTalk(int Mode, const char *Addressee, const char *Text, bool CheckSpamming);
|
||||
void ToDoChangeState(int NewState);
|
||||
|
||||
// VIRTUAL FUNCTIONS
|
||||
// =================
|
||||
@ -613,6 +646,9 @@ struct TPlayer: TCreature {
|
||||
|
||||
void CheckState(void);
|
||||
|
||||
void ClearPlayerkillingMarks(void);
|
||||
void SaveInventory(void);
|
||||
|
||||
// VIRTUAL FUNCTIONS
|
||||
// =================
|
||||
// TODO
|
||||
@ -658,19 +694,42 @@ struct TPlayer: TCreature {
|
||||
uint32 AddresseesTimes[20];
|
||||
};
|
||||
|
||||
// Creature API
|
||||
// crmain.cc
|
||||
// =============================================================================
|
||||
#define MAX_RACES 512
|
||||
|
||||
// crmain.cc
|
||||
extern TRaceData RaceData[MAX_RACES];
|
||||
extern int KilledCreatures[MAX_RACES];
|
||||
extern int KilledPlayers[MAX_RACES];
|
||||
extern priority_queue<uint32, uint32> ToDoQueue;
|
||||
|
||||
// TODO(fusion): These probably belong elsewhere but we should come back to
|
||||
// this when we're wrapping up creature files.
|
||||
bool IsCreaturePlayer(uint32 CreatureID);
|
||||
TCreature *GetCreature(uint32 CreatureID);
|
||||
TCreature *GetCreature(Object Obj);
|
||||
void InsertChainCreature(TCreature *Creature, int CoordX, int CoordY);
|
||||
void DeleteChainCreature(TCreature *Creature);
|
||||
void MoveChainCreature(TCreature *Creature, int CoordX, int CoordY);
|
||||
void ProcessCreatures(void);
|
||||
void ProcessSkills(void);
|
||||
void MoveCreatures(int Delay);
|
||||
|
||||
void AddKillStatistics(int AttackerRace, int DefenderRace);
|
||||
|
||||
bool IsRaceValid(int Race);
|
||||
int GetRaceByName(const char *RaceName);
|
||||
|
||||
// crnonpl.cc
|
||||
// =============================================================================
|
||||
void InitNonplayer();
|
||||
void ExitNonplayer();
|
||||
|
||||
// crplayer.cc
|
||||
// =============================================================================
|
||||
void InitPlayer(void);
|
||||
void ExitPlayer(void);
|
||||
|
||||
// crskill.cc
|
||||
// =============================================================================
|
||||
void InitCrskill(void);
|
||||
void ExitCrskill(void);
|
||||
|
||||
#endif //TIBIA_CREATURE_HH_
|
||||
|
||||
809
src/cract.cc
Normal file
809
src/cract.cc
Normal file
@ -0,0 +1,809 @@
|
||||
#include "cr.hh"
|
||||
|
||||
#include "stubs.hh"
|
||||
|
||||
// TShortway
|
||||
// =============================================================================
|
||||
struct TShortwayPoint {
|
||||
TShortwayPoint(void){
|
||||
this->x = 0;
|
||||
this->y = 0;
|
||||
this->Waypoints = -1;
|
||||
this->Waylength = -1;
|
||||
this->Heuristic = -1;
|
||||
this->Predecessor = NULL;
|
||||
this->NextToExpand = NULL;
|
||||
}
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int Waypoints;
|
||||
int Waylength;
|
||||
int Heuristic;
|
||||
TShortwayPoint *Predecessor;
|
||||
TShortwayPoint *NextToExpand;
|
||||
};
|
||||
|
||||
struct TShortway{
|
||||
TShortway(TCreature *Creature, int VisibleX, int VisibleY);
|
||||
~TShortway(void);
|
||||
void FillMap(void);
|
||||
void ClearMap(void);
|
||||
void Expand(TShortwayPoint *Node);
|
||||
bool Calculate(int DestX, int DestY, bool MustReach, int MaxSteps);
|
||||
|
||||
// DATA
|
||||
// =================
|
||||
matrix<TShortwayPoint> *Map;
|
||||
TShortwayPoint *FirstToExpand;
|
||||
TCreature *Creature;
|
||||
int VisibleX;
|
||||
int VisibleY;
|
||||
int StartX;
|
||||
int StartY;
|
||||
int StartZ;
|
||||
int MinWaypoints;
|
||||
};
|
||||
|
||||
TShortway::TShortway(TCreature *Creature, int VisibleX, int VisibleY){
|
||||
if(Creature == NULL){
|
||||
error("TShortway::TShortway: Übergebene Kreatur ist NULL.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(VisibleX < 1 || VisibleX > 100 || VisibleY < 1 || VisibleY > 100){
|
||||
error("TShortway::TShortway: Ungültiger Sichtbarkeitsbereich %d*%d.\n", VisibleX, VisibleY);
|
||||
return;
|
||||
}
|
||||
|
||||
this->Creature = Creature;
|
||||
this->VisibleX = VisibleX;
|
||||
this->VisibleY = VisibleY;
|
||||
this->StartX = Creature->posx;
|
||||
this->StartY = Creature->posy;
|
||||
this->StartZ = Creature->posz;
|
||||
this->Map = new matrix<TShortwayPoint>(
|
||||
-(VisibleX + 1), +(VisibleX + 1),
|
||||
-(VisibleY + 1), +(VisibleY + 1));
|
||||
this->FillMap();
|
||||
}
|
||||
|
||||
TShortway::~TShortway(void){
|
||||
if(this->Map != NULL){
|
||||
delete this->Map;
|
||||
}
|
||||
}
|
||||
|
||||
void TShortway::FillMap(void){
|
||||
this->MinWaypoints = 1000;
|
||||
|
||||
for(int X = -this->VisibleX; X <= this->VisibleX; X += 1)
|
||||
for(int Y = -this->VisibleY; Y <= this->VisibleY; Y += 1){
|
||||
int FieldX = this->StartX + X;
|
||||
int FieldY = this->StartY + Y;
|
||||
int FieldZ = this->StartZ;
|
||||
|
||||
int Waypoints = -1;
|
||||
Object Obj = GetFirstObject(FieldX, FieldY, FieldZ);
|
||||
if(Obj.exists()){
|
||||
ObjectType ObjType = Obj.getObjectType();
|
||||
if(ObjType.getFlag(BANK) && !ObjType.getFlag(UNPASS)){
|
||||
Waypoints = (int)ObjType.getAttribute(WAYPOINTS);
|
||||
if(Waypoints == 0){
|
||||
error("TShortway::FillMap: Ungültiger Wegpunkte-Wert %d für Bank %d.\n",
|
||||
Waypoints, ObjType.TypeID);
|
||||
Waypoints = -1;
|
||||
}
|
||||
|
||||
if(!this->Creature->MovePossible(FieldX, FieldY, FieldZ, false, false)){
|
||||
Waypoints = -1;
|
||||
}
|
||||
|
||||
if(Waypoints > 0 && Waypoints < this->MinWaypoints){
|
||||
this->MinWaypoints = Waypoints;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TShortwayPoint *Node = this->Map->at(X, Y);
|
||||
Node->x = X;
|
||||
Node->y = Y;
|
||||
Node->Waypoints = Waypoints;
|
||||
}
|
||||
}
|
||||
|
||||
void TShortway::ClearMap(void){
|
||||
for(int X = -this->VisibleX; X <= this->VisibleX; X += 1)
|
||||
for(int Y = -this->VisibleY; Y <= this->VisibleY; Y += 1){
|
||||
TShortwayPoint *Node = this->Map->at(X, Y);
|
||||
Node->Waylength = INT_MAX;
|
||||
Node->Heuristic = INT_MAX;
|
||||
Node->Predecessor = NULL;
|
||||
Node->NextToExpand = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void TShortway::Expand(TShortwayPoint *Node){
|
||||
if(Node == NULL){
|
||||
error("TShortway::Expand: Übergebener Knoten ist NULL.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
this->FirstToExpand = Node->NextToExpand;
|
||||
|
||||
int MinNeighborWaylength = Node->Waylength + Node->Waypoints;
|
||||
if(MinNeighborWaylength >= this->Map->at(0, 0)->Waylength){
|
||||
return;
|
||||
}
|
||||
|
||||
for(int OffsetX = -1; OffsetX <= 1; OffsetX += 1)
|
||||
for(int OffsetY = -1; OffsetY <= 1; OffsetY += 1){
|
||||
if(OffsetX == 0 && OffsetY == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
TShortwayPoint *Neighbor = this->Map->at(Node->x + OffsetX, Node->y + OffsetY);
|
||||
|
||||
// NOTE(fusion): The minimum neighbor waylength already contains the cost
|
||||
// of a single step. Diagonal steps are three times more expensive so we
|
||||
// add waypoints two more times.
|
||||
int NeighborWaylength = MinNeighborWaylength;
|
||||
if(OffsetX != 0 && OffsetY != 0){
|
||||
NeighborWaylength += Node->Waylength * 2;
|
||||
}
|
||||
|
||||
if(NeighborWaylength < Neighbor->Waylength){
|
||||
Neighbor->Waylength = NeighborWaylength;
|
||||
Neighbor->Predecessor = Node;
|
||||
if((Neighbor->x != 0 || Neighbor->y != 0) && Neighbor->Waypoints != -1){
|
||||
// NOTE(fusion): Remove from expand list if it was already expanded upon.
|
||||
if(Neighbor->Heuristic != INT_MAX){
|
||||
TShortwayPoint *Prev = NULL;
|
||||
TShortwayPoint *Cur = this->FirstToExpand;
|
||||
while(Cur != NULL && Cur != Neighbor){
|
||||
Prev = Cur;
|
||||
Cur = Cur->NextToExpand;
|
||||
}
|
||||
|
||||
if(Cur != Neighbor){
|
||||
error("TShortway::Expand: Knoten steht nicht in der ExpandList.\n");
|
||||
}else if(Prev == NULL){
|
||||
this->FirstToExpand = Neighbor->NextToExpand;
|
||||
}else{
|
||||
Prev->NextToExpand = Neighbor->NextToExpand;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(fusion): Compute heuristic using the manhattan distance.
|
||||
int Distance = std::abs(Neighbor->x) + std::abs(Neighbor->y);
|
||||
Neighbor->Heuristic = Neighbor->Waylength
|
||||
+ Neighbor->Waypoints * 1
|
||||
+ this->MinWaypoints * (Distance - 1);
|
||||
|
||||
// NOTE(fusion): Insert into expand list.
|
||||
{
|
||||
TShortwayPoint *Prev = NULL;
|
||||
TShortwayPoint *Cur = this->FirstToExpand;
|
||||
while(Cur != NULL && Cur->Heuristic < Neighbor->Heuristic){
|
||||
Prev = Cur;
|
||||
Cur = Cur->NextToExpand;
|
||||
}
|
||||
|
||||
if(Prev == NULL){
|
||||
this->FirstToExpand = Neighbor;
|
||||
}else{
|
||||
Prev->NextToExpand = Neighbor;
|
||||
}
|
||||
Neighbor->NextToExpand = Cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TShortway::Calculate(int DestX, int DestY, bool MustReach, int MaxSteps){
|
||||
if(this->Map == NULL){
|
||||
error("TShortway::Calculate: Karte existiert nicht.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE(fusion): Transform dest to relative coordinates.
|
||||
DestX -= this->StartX;
|
||||
DestY -= this->StartY;
|
||||
|
||||
// NOTE(fusion): Check if already at destination.
|
||||
if(DestX == 0 && DestY == 0){
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE(fusion): Check if out of range.
|
||||
if(std::abs(DestX) > this->VisibleX || std::abs(DestY) > this->VisibleY){
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE(fusion): Find shortest path from the destination to the origin.
|
||||
this->ClearMap();
|
||||
this->FirstToExpand = this->Map->at(DestX, DestY);
|
||||
this->FirstToExpand->Waylength = 0;
|
||||
while(this->FirstToExpand != NULL){
|
||||
this->Expand(this->FirstToExpand);
|
||||
}
|
||||
|
||||
// NOTE(fusion): Check if the origin was reached from the destination.
|
||||
TShortwayPoint *Node = this->Map->at(0, 0);
|
||||
if(Node->Waylength == INT_MAX){
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE(fusion): Walk back from the origin to reconstruct the path.
|
||||
Node = Node->Predecessor;
|
||||
while(Node != NULL && MaxSteps > 0){
|
||||
int MaxDistance = std::max<int>(
|
||||
std::abs(Node->x - DestX),
|
||||
std::abs(Node->y - DestY));
|
||||
if(!MustReach && MaxDistance <= 1){
|
||||
break;
|
||||
}
|
||||
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDGo;
|
||||
TD.Go.x = this->StartX + Node->x;
|
||||
TD.Go.y = this->StartY + Node->y;
|
||||
TD.Go.z = this->StartZ;
|
||||
Creature->ToDoAdd(TD);
|
||||
|
||||
Node = Node->Predecessor;
|
||||
MaxSteps -= 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TCreature
|
||||
// =============================================================================
|
||||
bool TCreature::SetOnMap(void){
|
||||
int LoginX = this->posx;
|
||||
int LoginY = this->posy;
|
||||
int LoginZ = this->posz;
|
||||
bool Player = (this->Type == PLAYER);
|
||||
if(!SearchLoginField(&LoginX, &LoginY, &LoginZ, 1, Player)){
|
||||
bool Found = false;
|
||||
if(IsHouse(LoginX, LoginY, LoginZ)){
|
||||
uint16 HouseID = GetHouseID(LoginX, LoginY, LoginZ);
|
||||
GetExitPosition(HouseID, &LoginX, &LoginY, &LoginZ);
|
||||
Found = SearchLoginField(&LoginX, &LoginY, &LoginZ, 1, Player);
|
||||
}
|
||||
|
||||
if(!Found){
|
||||
LoginX = this->startx;
|
||||
LoginY = this->starty;
|
||||
LoginZ = this->startz;
|
||||
}
|
||||
}
|
||||
|
||||
Object Con = GetMapContainer(LoginX, LoginY, LoginZ);
|
||||
if(Con == NONE){
|
||||
error("TCreature::SetOnMap: Kartencontainer für Punkt [%d,%d,%d] existiert nicht.\n",
|
||||
LoginX, LoginY, LoginZ);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->posx = LoginX;
|
||||
this->posy = LoginY;
|
||||
this->posz = LoginZ;
|
||||
|
||||
// NOTE(fusion): `Create` automatically sets `this->CrObject` and creates
|
||||
// its body container slots.
|
||||
bool Result = true;
|
||||
try{
|
||||
Create(Con, TYPEID_CREATURE_CONTAINER, this->ID);
|
||||
}catch(RESULT r){
|
||||
error("TCreature::SetOnMap: Kann Kreatur nicht setzen ([%d,%d,%d] - Exception %d).\n",
|
||||
LoginX, LoginY, LoginZ, r);
|
||||
if(this->Type == PLAYER){
|
||||
SendResult(this->Connection, r);
|
||||
}
|
||||
Result = false;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool TCreature::DelOnMap(void){
|
||||
Object Obj = this->CrObject;
|
||||
if(Obj == NONE){
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(fusion): I feel `Delete` should also manage `this->CrObject` automatically.
|
||||
bool Result = true;
|
||||
this->CrObject = NONE;
|
||||
try{
|
||||
Delete(Obj, -1);
|
||||
}catch(RESULT r){
|
||||
error("TCreature::DelOnMap: Error Deleting CreatureObject: %d\n", r);
|
||||
Result = false;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void TCreature::Attack(void){
|
||||
this->Combat.Attack();
|
||||
}
|
||||
|
||||
//void TCreature::Execute(void);
|
||||
|
||||
uint32 TCreature::CalculateDelay(void){
|
||||
uint32 Delay = 0;
|
||||
TToDoEntry *TD = this->ToDoList.at(this->ActToDo);
|
||||
switch(TD->Code){
|
||||
case TDWait:{
|
||||
// TODO(fusion): I'm not sure about having `EarliestWalkTime` here.
|
||||
uint32 WaitTime = TD->Wait.Time;
|
||||
if(WaitTime < this->EarliestWalkTime){
|
||||
WaitTime = this->EarliestWalkTime;
|
||||
}
|
||||
|
||||
if(WaitTime > ServerMilliseconds){
|
||||
Delay = WaitTime - ServerMilliseconds;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TDGo:{
|
||||
if(this->EarliestWalkTime > ServerMilliseconds){
|
||||
Delay = this->EarliestWalkTime - ServerMilliseconds;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TDUse:{
|
||||
if(TD->Use.Obj2 != 0){
|
||||
if(this->EarliestMultiuseTime > ServerMilliseconds){
|
||||
Delay = this->EarliestMultiuseTime - ServerMilliseconds;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TDAttack:{
|
||||
uint32 EarliestAttackTime = this->Combat.EarliestAttackTime;
|
||||
if(EarliestAttackTime < this->EarliestSpellTime){
|
||||
EarliestAttackTime = this->EarliestSpellTime;
|
||||
}
|
||||
|
||||
if(EarliestAttackTime > ServerMilliseconds){
|
||||
Delay = EarliestAttackTime - ServerMilliseconds;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Delay;
|
||||
}
|
||||
|
||||
bool TCreature::ToDoClear(void){
|
||||
bool SnapbackNecessary = false;
|
||||
for(int i = 0; i < this->NrToDo; i += 1){
|
||||
TToDoEntry *TD = this->ToDoList.at(i);
|
||||
switch(TD->Code){
|
||||
case TDGo:{
|
||||
if(this->ActToDo <= i){
|
||||
SnapbackNecessary = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TDTalk:{
|
||||
DeleteDynamicString(TD->Talk.Text);
|
||||
DeleteDynamicString(TD->Talk.Addressee);
|
||||
break;
|
||||
}
|
||||
|
||||
case TDChangeState:{
|
||||
if(this->ActToDo <= i && this->Type == NPC){
|
||||
ChangeNPCState(this, TD->ChangeState.NewState, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->LockToDo = false;
|
||||
this->ActToDo = 0;
|
||||
this->NrToDo = 0;
|
||||
this->Stop = false;
|
||||
return SnapbackNecessary;
|
||||
}
|
||||
|
||||
void TCreature::ToDoAdd(TToDoEntry TD){
|
||||
if(this->LockToDo){
|
||||
if(this->ToDoClear() && this->Type == PLAYER){
|
||||
SendSnapback(this->Connection);
|
||||
}
|
||||
}
|
||||
|
||||
*this->ToDoList.at(this->NrToDo) = TD;
|
||||
this->NrToDo += 1;
|
||||
}
|
||||
|
||||
void TCreature::ToDoStop(void){
|
||||
if(this->LockToDo){
|
||||
this->Stop = true;
|
||||
}else if(this->Type == PLAYER){
|
||||
SendSnapback(this->Connection);
|
||||
}
|
||||
}
|
||||
|
||||
void TCreature::ToDoStart(void){
|
||||
if(this->NrToDo != 0){
|
||||
this->LockToDo = true;
|
||||
this->ActToDo = 0;
|
||||
|
||||
uint32 Delay = this->CalculateDelay();
|
||||
if(Delay < 1){
|
||||
Delay = 1;
|
||||
}
|
||||
|
||||
uint32 NextWakeup = ServerMilliseconds + Delay;
|
||||
ToDoQueue.insert(NextWakeup, this->ID);
|
||||
this->NextWakeup = NextWakeup;
|
||||
}
|
||||
}
|
||||
|
||||
void TCreature::ToDoYield(void){
|
||||
if(!this->LockToDo){
|
||||
this->ToDoWait(0);
|
||||
this->ToDoStart();
|
||||
}
|
||||
}
|
||||
|
||||
void TCreature::ToDoWait(int Delay){
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDWait;
|
||||
TD.Wait.Time = ServerMilliseconds + Delay;
|
||||
this->ToDoAdd(TD);
|
||||
}
|
||||
|
||||
void TCreature::ToDoGo(int DestX, int DestY, int DestZ, bool MustReach, int MaxSteps){
|
||||
if(this->posz < DestZ){
|
||||
throw DOWNSTAIRS;
|
||||
}else if(this->posz > DestZ){
|
||||
throw UPSTAIRS;
|
||||
}
|
||||
|
||||
if(this->LockToDo){
|
||||
TToDoEntry *Last = this->ToDoList.at(this->NrToDo - 1);
|
||||
if(Last->Code == TDGo
|
||||
&& Last->Go.x == DestX
|
||||
&& Last->Go.y == DestY
|
||||
&& Last->Go.z == DestZ){
|
||||
// TODO(fusion): Why? Shouldn't we just return here?
|
||||
throw NOERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int DistanceX = std::abs(DestX - this->posx);
|
||||
int DistanceY = std::abs(DestY - this->posy);
|
||||
int MaxDistance = std::max<int>(DistanceX, DistanceY);
|
||||
if(MaxDistance == 0 || (!MustReach && MaxDistance <= 1)){
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE(fusion): The number of steps between two points is the same as the
|
||||
// their manhattan distance, if we exclude diagonal movement. We can skip
|
||||
// the path finder if we know we're step away from the destination.
|
||||
if(DistanceX + DistanceY == 1){
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDGo;
|
||||
TD.Go.x = DestX;
|
||||
TD.Go.y = DestY;
|
||||
TD.Go.z = DestZ;
|
||||
this->ToDoAdd(TD);
|
||||
}else{
|
||||
int VisibleX = (this->Type == PLAYER) ? 7 : 10;
|
||||
int VisibleY = (this->Type == PLAYER) ? 7 : 10;
|
||||
TShortway Shortway(this, VisibleX, VisibleY);
|
||||
if(!Shortway.Calculate(DestX, DestY, MustReach, MaxSteps)){
|
||||
this->ToDoClear();
|
||||
if(this->Type == PLAYER){
|
||||
SendSnapback(this->Connection);
|
||||
}
|
||||
throw NOWAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TCreature::ToDoRotate(int Direction){
|
||||
if(Direction != DIRECTION_NORTH
|
||||
&& Direction != DIRECTION_EAST
|
||||
&& Direction != DIRECTION_SOUTH
|
||||
&& Direction != DIRECTION_WEST){
|
||||
throw ERROR;
|
||||
}
|
||||
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDRotate;
|
||||
TD.Rotate.Direction = Direction;
|
||||
this->ToDoAdd(TD);
|
||||
}
|
||||
|
||||
void TCreature::ToDoMove(int ObjX, int ObjY, int ObjZ, ObjectType Type, uint8 RNum,
|
||||
int DestX, int DestY, int DestZ, uint8 Count){
|
||||
Object Obj = GetObject(this->ID, ObjX, ObjY, ObjZ, RNum, Type);
|
||||
if(!Obj.exists()){
|
||||
throw NOTACCESSIBLE;
|
||||
}
|
||||
|
||||
// TODO(fusion): This could be an inlined function.
|
||||
if(ObjX != 0xFFFF){ // SPECIAL_COORDINATE ?
|
||||
if(this->posz > ObjZ){
|
||||
throw UPSTAIRS;
|
||||
}else if(this->posz < ObjZ){
|
||||
throw DOWNSTAIRS;
|
||||
}
|
||||
|
||||
if(!ObjectInRange(this->ID, Obj, 1)){
|
||||
this->ToDoGo(ObjX, ObjY, ObjZ, false, INT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
int Delay = 100;
|
||||
if(Obj.getObjectType().isCreatureContainer()){
|
||||
Object DestBank = GetFirstObject(DestX, DestY, DestZ);
|
||||
if(DestBank == NONE || !DestBank.getObjectType().getFlag(BANK)){
|
||||
throw NOTACCESSIBLE;
|
||||
}
|
||||
|
||||
TCreature *Creature = GetCreature(Obj);
|
||||
if(Creature == NULL){
|
||||
error("TCreature::ToDoMove: Kreatur existiert nicht.\n");
|
||||
throw ERROR;
|
||||
}
|
||||
|
||||
Delay = 1000;
|
||||
if(this->EarliestWalkTime > ServerMilliseconds){
|
||||
Delay += (int)(this->EarliestWalkTime - ServerMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
this->ToDoWait(Delay);
|
||||
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDMove;
|
||||
TD.Move.Obj = Obj.ObjectID;
|
||||
TD.Move.x = DestX;
|
||||
TD.Move.y = DestY;
|
||||
TD.Move.z = DestZ;
|
||||
TD.Move.Count = Count;
|
||||
this->ToDoAdd(TD);
|
||||
}
|
||||
|
||||
void TCreature::ToDoMove(Object Obj, int DestX, int DestY, int DestZ, uint8 Count){
|
||||
if(!Obj.exists()){
|
||||
throw NOTACCESSIBLE;
|
||||
}
|
||||
|
||||
if(!ObjectInRange(this->ID, Obj, 1)){
|
||||
this->ToDoGo(DestX, DestY, DestZ, false, INT_MAX);
|
||||
}
|
||||
|
||||
int Delay = 100;
|
||||
if(Obj.getObjectType().isCreatureContainer()){
|
||||
// TODO(fusion): We don't add the delay until the earliest walk time
|
||||
// like in the other variant above.
|
||||
Delay = 1000;
|
||||
}
|
||||
|
||||
this->ToDoWait(Delay);
|
||||
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDMove;
|
||||
TD.Move.Obj = Obj.ObjectID;
|
||||
TD.Move.x = DestX;
|
||||
TD.Move.y = DestY;
|
||||
TD.Move.z = DestZ;
|
||||
TD.Move.Count = Count;
|
||||
this->ToDoAdd(TD);
|
||||
}
|
||||
|
||||
void TCreature::ToDoTrade(int ObjX, int ObjY, int ObjZ, ObjectType Type, uint8 RNum,
|
||||
uint32 TradePartner){
|
||||
Object Obj = GetObject(this->ID, ObjX, ObjY, ObjZ, RNum, Type);
|
||||
if(!Obj.exists()){
|
||||
throw NOTACCESSIBLE;
|
||||
}
|
||||
|
||||
ObjectType ObjType = Obj.getObjectType();
|
||||
if(ObjType.getFlag(UNMOVE)){
|
||||
throw NOTMOVABLE;
|
||||
}
|
||||
|
||||
if(!ObjType.getFlag(TAKE)){
|
||||
throw NOTTAKABLE;
|
||||
}
|
||||
|
||||
if(ObjType.isCreatureContainer()){
|
||||
error("TCreature::ToDoTrade: Objekt ist eine Kreatur.\n");
|
||||
throw ERROR;
|
||||
}
|
||||
|
||||
if(TradePartner == 0){
|
||||
error("TCreature::ToDoTrade: Handelspartner ist Null.\n");
|
||||
throw ERROR;
|
||||
}
|
||||
|
||||
TCreature *Creature = GetCreature(TradePartner);
|
||||
if(Creature == NULL){
|
||||
throw PLAYERNOTONLINE;
|
||||
}
|
||||
|
||||
if(Creature->Type != PLAYER){
|
||||
error("TCreature::ToDoTrade: Handelspartner von %s ist kein Spieler.\n", this->Name);
|
||||
throw ERROR;
|
||||
}
|
||||
|
||||
// TODO(fusion): This could be an inlined function.
|
||||
if(ObjX != 0xFFFF){ // SPECIAL_COORDINATE ?
|
||||
if(this->posz > ObjZ){
|
||||
throw UPSTAIRS;
|
||||
}else if(this->posz < ObjZ){
|
||||
throw DOWNSTAIRS;
|
||||
}
|
||||
|
||||
if(!ObjectInRange(this->ID, Obj, 1)){
|
||||
this->ToDoGo(ObjX, ObjY, ObjZ, false, INT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDTrade;
|
||||
TD.Trade.Obj = Obj.ObjectID;
|
||||
TD.Trade.Partner = TradePartner;
|
||||
this->ToDoAdd(TD);
|
||||
}
|
||||
|
||||
void TCreature::ToDoUse(uint8 Count, int ObjX1, int ObjY1, int ObjZ1, ObjectType Type1, uint8 RNum1,
|
||||
uint8 Dummy, int ObjX2, int ObjY2, int ObjZ2, ObjectType Type2, uint8 RNum2){
|
||||
Object Obj1 = GetObject(this->ID, ObjX1, ObjY1, ObjZ1, RNum1, Type1);
|
||||
if(!Obj1.exists()){
|
||||
throw NOTACCESSIBLE;
|
||||
}
|
||||
|
||||
Object Obj2 = NONE;
|
||||
if(Count >= 2){
|
||||
Obj2 = GetObject(this->ID, ObjX2, ObjY2, ObjZ2, RNum2, Type2);
|
||||
if(!Obj2.exists()){
|
||||
throw NOTACCESSIBLE;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(fusion): This could be an inlined function.
|
||||
if(ObjX1 != 0xFFFF){ // SPECIAL_COORDINATE ?
|
||||
if(this->posz > ObjZ1){
|
||||
throw UPSTAIRS;
|
||||
}else if(this->posz < ObjZ1){
|
||||
throw DOWNSTAIRS;
|
||||
}
|
||||
|
||||
if(!ObjectInRange(this->ID, Obj1, 1)){
|
||||
this->ToDoGo(ObjX1, ObjY1, ObjZ1, false, INT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
this->ToDoWait(100);
|
||||
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDUse;
|
||||
TD.Use.Obj1 = Obj1.ObjectID;
|
||||
TD.Use.Obj2 = Obj2.ObjectID;
|
||||
// TODO(fusion): This is the value sent by the client on single object use
|
||||
// that contains the next free container index.
|
||||
TD.Use.Dummy = Dummy;
|
||||
this->ToDoAdd(TD);
|
||||
}
|
||||
|
||||
void TCreature::ToDoUse(uint8 Count, Object Obj1, Object Obj2){
|
||||
if(!Obj1.exists()){
|
||||
throw NOTACCESSIBLE;
|
||||
}
|
||||
|
||||
if(Count <= 1){
|
||||
Obj2 = NONE;
|
||||
}else if(!Obj2.exists()){
|
||||
throw NOTACCESSIBLE;
|
||||
}
|
||||
|
||||
if(!ObjectInRange(this->ID, Obj1, 1)){
|
||||
int ObjX1, ObjY1, ObjZ1;
|
||||
GetObjectCoordinates(Obj1, &ObjX1, &ObjY1, &ObjZ1);
|
||||
this->ToDoGo(ObjX1, ObjY1, ObjZ1, false, INT_MAX);
|
||||
}
|
||||
|
||||
this->ToDoWait(100);
|
||||
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDUse;
|
||||
TD.Use.Obj1 = Obj1.ObjectID;
|
||||
TD.Use.Obj2 = Obj2.ObjectID;
|
||||
// TODO(fusion): Not sure why we set this value at all.
|
||||
TD.Use.Dummy = Count;
|
||||
this->ToDoAdd(TD);
|
||||
}
|
||||
|
||||
void TCreature::ToDoTurn(int ObjX, int ObjY, int ObjZ, ObjectType Type, uint8 RNum){
|
||||
Object Obj = GetObject(this->ID, ObjX, ObjY, ObjZ, RNum, Type);
|
||||
if(!Obj.exists()){
|
||||
throw NOTACCESSIBLE;
|
||||
}
|
||||
|
||||
// TODO(fusion): This could be an inlined function.
|
||||
if(ObjX != 0xFFFF){ // SPECIAL_COORDINATE ?
|
||||
if(this->posz > ObjZ){
|
||||
throw UPSTAIRS;
|
||||
}else if(this->posz < ObjZ){
|
||||
throw DOWNSTAIRS;
|
||||
}
|
||||
|
||||
if(!ObjectInRange(this->ID, Obj, 1)){
|
||||
this->ToDoGo(ObjX, ObjY, ObjZ, false, INT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
this->ToDoWait(100);
|
||||
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDTurn;
|
||||
TD.Turn.Obj = Obj.ObjectID;
|
||||
this->ToDoAdd(TD);
|
||||
}
|
||||
|
||||
void TCreature::ToDoAttack(void){
|
||||
this->Combat.CanToDoAttack();
|
||||
if(this->Combat.GetDistance() != 1){
|
||||
this->ToDoWait(100);
|
||||
}
|
||||
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDAttack;
|
||||
this->ToDoAdd(TD);
|
||||
}
|
||||
|
||||
void TCreature::ToDoTalk(int Mode, const char *Addressee, const char *Text, bool CheckSpamming){
|
||||
TToDoEntry TD = {};
|
||||
|
||||
if(Text == NULL || Text[0] == 0){
|
||||
error("TCreature::ToDoTalk: Text ist NULL bei %s.\n", this->Name);
|
||||
|
||||
// TODO(fusion): The original code would attempt to call `AddDynamicString`
|
||||
// even after this check but it doesn't check whether `Text` is NULL and
|
||||
// calls `strlen` immediately on it which could be a problem.
|
||||
TD.Talk.Text = 0;
|
||||
}else{
|
||||
TD.Talk.Text = AddDynamicString(Text);
|
||||
}
|
||||
|
||||
if(Addressee == NULL){
|
||||
TD.Talk.Addressee = 0;
|
||||
}else{
|
||||
TD.Talk.Addressee = AddDynamicString(Addressee);
|
||||
}
|
||||
|
||||
TD.Code = TDTalk;
|
||||
TD.Talk.Mode = Mode;
|
||||
TD.Talk.CheckSpamming = CheckSpamming;
|
||||
this->ToDoAdd(TD);
|
||||
}
|
||||
|
||||
void TCreature::ToDoChangeState(int NewState){
|
||||
TToDoEntry TD = {};
|
||||
TD.Code = TDChangeState;
|
||||
TD.ChangeState.NewState = NewState;
|
||||
this->ToDoAdd(TD);
|
||||
}
|
||||
@ -307,11 +307,11 @@ int TCombat::GetArmorStrength(void){
|
||||
int TCombat::GetDistance(void){
|
||||
int Distance = 0;
|
||||
if(this->Close != NONE || this->Fist){
|
||||
Distance = 1;
|
||||
Distance = 1; // DISTANCE_CLOSE ?
|
||||
}else if(this->Throw != NONE){
|
||||
Distance = 2;
|
||||
Distance = 2; // DISTANCE_THROW ?
|
||||
}else if(this->Missile != NONE || this->Wand != NONE){
|
||||
Distance = 3;
|
||||
Distance = 3; // DISTANCE_RANGE ?
|
||||
}
|
||||
return Distance;
|
||||
}
|
||||
|
||||
581
src/crmain.cc
581
src/crmain.cc
@ -1,22 +1,23 @@
|
||||
#include "cr.hh"
|
||||
#include "config.hh"
|
||||
#include "enums.hh"
|
||||
#include "info.hh"
|
||||
|
||||
#include "stubs.hh"
|
||||
|
||||
static uint32 NextCreatureID;
|
||||
static matrix<uint32> *FirstChainCreature;
|
||||
static TCreature *HashList[1000];
|
||||
|
||||
static int FirstFreeCreature;
|
||||
static vector<TCreature*> CreatureList(0, 10000, 1000);
|
||||
|
||||
static priority_queue<uint32, uint32> ToDoQueue(5000, 1000);
|
||||
//static priority_queue<uint32, TAttackWave*> AttackWaveQueue(100, 100);
|
||||
|
||||
TRaceData RaceData[MAX_RACES];
|
||||
int KilledCreatures[MAX_RACES];
|
||||
int KilledPlayers[MAX_RACES];
|
||||
|
||||
priority_queue<uint32, uint32> ToDoQueue(5000, 1000);
|
||||
//priority_queue<uint32, TAttackWave*> AttackWaveQueue(100, 100);
|
||||
|
||||
static uint32 NextCreatureID;
|
||||
static int FirstFreeCreature;
|
||||
static TCreature *HashList[1000];
|
||||
static matrix<uint32> *FirstChainCreature;
|
||||
static vector<TCreature*> CreatureList(0, 10000, 1000);
|
||||
|
||||
// TCreature
|
||||
// =============================================================================
|
||||
TCreature::TCreature(void) :
|
||||
@ -67,8 +68,286 @@ TCreature::TCreature(void) :
|
||||
}
|
||||
}
|
||||
|
||||
void TCreature::Attack(void){
|
||||
this->Combat.Attack();
|
||||
TCreature::~TCreature(void){
|
||||
// TODO(fusion): Bruuhh... these exceptions...
|
||||
if(this->IsDead){
|
||||
int Race = this->Race;
|
||||
int PoolLiquid = LIQUID_NONE;
|
||||
if(RaceData[Race].Blood == BT_BLOOD){
|
||||
PoolLiquid = LIQUID_BLOOD;
|
||||
}else if(RaceData[Race].Blood == BT_SLIME){
|
||||
PoolLiquid = LIQUID_SLIME;
|
||||
}
|
||||
|
||||
if(PoolLiquid != LIQUID_NONE){
|
||||
try{
|
||||
CreatePool(GetMapContainer(this->CrObject),
|
||||
GetSpecialObject(BLOOD_POOL),
|
||||
PoolLiquid);
|
||||
}catch(RESULT r){
|
||||
if(r != NOROOM && r != DESTROYED){
|
||||
error("TCreature::~TCreature: Kann Blutlache nicht setzen (Exc %d, Pos [%d,%d,%d]).\n",
|
||||
r, this->posx, this->posy, this->posz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectType CorpseType = (this->Sex == 1) // MALE ?
|
||||
? RaceData[Race].MaleCorpse
|
||||
: RaceData[Race].FemaleCorpse;
|
||||
|
||||
if(CorpseType.getFlag(MAGICFIELD)){
|
||||
Object Obj = GetFirstObject(this->posx, this->posy, this->posz);
|
||||
while(Obj != NONE){
|
||||
Object Next = Obj.getNextObject();
|
||||
if(Obj.getObjectType().getFlag(MAGICFIELD)){
|
||||
try{
|
||||
Delete(Obj, -1);
|
||||
}catch(RESULT r){
|
||||
error("TCreature::~TCreature: Exception %d beim Löschen eines Feldes.\n", r);
|
||||
}
|
||||
}
|
||||
Obj = Next;
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
Object Con = GetMapContainer(this->posx, this->posy, this->posz);
|
||||
Object Corpse = Create(Con, CorpseType, 0);
|
||||
Log("game", "Tod von %s: LoseInventory=%d.\n", this->Name, this->LoseInventory);
|
||||
|
||||
if(this->Type == PLAYER){
|
||||
char Help[128];
|
||||
sprintf(Help, "You recognize %s", this->Name);
|
||||
if(this->Murderer[0] != 0){
|
||||
if(this->Sex == 1){ // MALE ?
|
||||
strcat(Help, ". He was killed by ");
|
||||
}else{
|
||||
strcat(Help, ". She was killed by ");
|
||||
}
|
||||
strcat(Help, this->Murderer);
|
||||
}
|
||||
Change(Corpse, TEXTSTRING, AddDynamicString(Help));
|
||||
}
|
||||
|
||||
if(this->LoseInventory != 0){ // LOSE_INVENTORY_NONE ?
|
||||
for(int Position = 1; Position <= 10; Position += 1){
|
||||
Object Item = GetBodyObject(this->ID, Position);
|
||||
if(Item == NONE){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(this->LoseInventory != 2 // LOSE_INVENTORY_ALL ?
|
||||
&& !Item.getObjectType().getFlag(CONTAINER)
|
||||
&& random(0, 9) != 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
Move(0, Item, Corpse, -1, false, NONE);
|
||||
}
|
||||
}
|
||||
|
||||
if(this->Type == PLAYER && this->LoseInventory != 2){ // LOSE_INVENTORY_ALL ?
|
||||
((TPlayer*)this)->SaveInventory();
|
||||
}
|
||||
}catch(RESULT r){
|
||||
error("TCreature::~TCreature: Kann Leiche/Inventory nicht ablegen (Exc %d, Pos [%d,%d,%d], %s).\n",
|
||||
r, this->posx, this->posy, this->posz, this->Name);
|
||||
}
|
||||
}
|
||||
|
||||
if(this->CrObject != NONE && this->CrObject.exists()){
|
||||
this->DelOnMap();
|
||||
}
|
||||
|
||||
this->ToDoClear();
|
||||
|
||||
if(this->Type == PLAYER && this->Connection != NULL){
|
||||
this->Connection->Logout(30, true);
|
||||
}
|
||||
|
||||
this->DelInCrList();
|
||||
|
||||
if(this->ID != 0){
|
||||
this->DelID();
|
||||
}
|
||||
|
||||
for(TKnownCreature *KnownCreature = this->FirstKnowingConnection;
|
||||
KnownCreature != NULL;
|
||||
KnownCreature = KnownCreature->Next){
|
||||
if(KnownCreature->CreatureID != this->ID){
|
||||
error("TCreature::~TCreature: Verkettungsfehler bei Kreatur %u.\n", this->ID);
|
||||
}
|
||||
KnownCreature->State = KNOWNCREATURE_FREE;
|
||||
}
|
||||
}
|
||||
|
||||
void TCreature::SetID(uint32 CharacterID){
|
||||
if(this->ID != 0){
|
||||
error("TCreature::SetID: ID ist schon gesetzt.\n");
|
||||
}
|
||||
|
||||
uint32 CreatureID = 0;
|
||||
if(CharacterID == 0){
|
||||
bool Found = false;
|
||||
for(int Attempts = 0; Attempts < 16; Attempts += 1){
|
||||
CreatureID = NextCreatureID++;
|
||||
if(GetCreature(CreatureID) == NULL){
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!Found){
|
||||
error("TCreature::SetID: 16x hintereinander doppelte ID."
|
||||
" Verwende nun doppelte ID %d\n", CreatureID);
|
||||
}
|
||||
}else{
|
||||
CreatureID = CharacterID;
|
||||
if(GetCreature(CreatureID) != NULL){
|
||||
error("TCreature::SetID: Doppelte Character-ID %d gefunden.\n", CharacterID);
|
||||
}
|
||||
}
|
||||
|
||||
uint32 ListIndex = CreatureID % NARRAY(HashList);
|
||||
this->ID = CreatureID;
|
||||
this->NextHashEntry = HashList[ListIndex];
|
||||
HashList[ListIndex] = this;
|
||||
}
|
||||
|
||||
void TCreature::DelID(void){
|
||||
uint32 ListIndex = this->ID % NARRAY(HashList);
|
||||
TCreature *First = HashList[ListIndex];
|
||||
if(First == NULL){
|
||||
error("TCreature::DelID: Hasheintrag nicht gefunden id = %d\n", this->ID);
|
||||
return;
|
||||
}
|
||||
|
||||
if(First->ID == this->ID){
|
||||
HashList[ListIndex] = this->NextHashEntry;
|
||||
}else{
|
||||
TCreature *Prev = First;
|
||||
TCreature *Current = First->NextHashEntry;
|
||||
while(true){
|
||||
if(Current == NULL){
|
||||
error("TCreature::DelID: id=%d nicht gefunden.\n", this->ID);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Current->ID == this->ID){
|
||||
Prev->NextHashEntry = this->NextHashEntry;
|
||||
break;
|
||||
}
|
||||
|
||||
Prev = Current;
|
||||
Current = Current->NextHashEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TCreature::SetInCrList(void){
|
||||
*CreatureList.at(FirstFreeCreature) = this;
|
||||
FirstFreeCreature += 1;
|
||||
}
|
||||
|
||||
void TCreature::DelInCrList(void){
|
||||
// TODO(fusion): See note in `ProcessCreatures`.
|
||||
for(int Index = 0; Index < FirstFreeCreature; Index += 1){
|
||||
TCreature **Current = CreatureList.at(Index);
|
||||
if(*Current == this){
|
||||
TCreature **Last = CreatureList.at(FirstFreeCreature - 1);
|
||||
*Current = *Last;
|
||||
*Last = NULL;
|
||||
FirstFreeCreature -= 1;
|
||||
|
||||
// TODO(fusion): The original function wouldn't break here. Maybe it
|
||||
// is possible to have duplicates in `CreatureList`?
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TCreature::StartLogout(bool Force, bool StopFight){
|
||||
this->LoggingOut = true;
|
||||
if(Force || LagDetected()){
|
||||
this->LogoutAllowed = true;
|
||||
}
|
||||
|
||||
if(this->Type == PLAYER && this->Connection != NULL){
|
||||
this->Connection->Logout(0, true);
|
||||
}
|
||||
|
||||
this->Combat.StopAttack(StopFight ? 0 : 60);
|
||||
}
|
||||
|
||||
int TCreature::LogoutPossible(void){
|
||||
if(!this->LogoutAllowed && !this->IsDead && !GameEnding()){
|
||||
if(this->EarliestLogoutRound > RoundNr && !LagDetected()){
|
||||
return 1; // LOGOUT_COMBAT ?
|
||||
}
|
||||
|
||||
if(IsNoLogoutField(this->posx, this->posy, this->posz)){
|
||||
return 2; // LOGOUT_FIELD ?
|
||||
}
|
||||
|
||||
this->LogoutAllowed = true;
|
||||
}
|
||||
|
||||
return 0; // LOGOUT_OK ?
|
||||
}
|
||||
|
||||
void TCreature::BlockLogout(int Delay, bool BlockProtectionZone){
|
||||
if(WorldType == NON_PVP){
|
||||
BlockProtectionZone = false;
|
||||
}
|
||||
|
||||
if(this->Type == PLAYER && !CheckRight(this->ID, NO_LOGOUT_BLOCK)){
|
||||
if(BlockProtectionZone || this->EarliestProtectionZoneRound > RoundNr){
|
||||
uint32 EarliestProtectionZoneRound = RoundNr + Delay;
|
||||
if(this->EarliestProtectionZoneRound < EarliestProtectionZoneRound){
|
||||
this->EarliestProtectionZoneRound = EarliestProtectionZoneRound;
|
||||
}
|
||||
}else if(this->Connection == NULL){
|
||||
// NOTE(fusion): This is a failsafe to avoid extending the earliest
|
||||
// logout round of a player that got disconnected in combat.
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 EarliestLogoutRound = RoundNr + Delay;
|
||||
if(this->EarliestLogoutRound < EarliestLogoutRound){
|
||||
this->EarliestLogoutRound = EarliestLogoutRound;
|
||||
}
|
||||
|
||||
((TPlayer*)this)->CheckState();
|
||||
}
|
||||
}
|
||||
|
||||
int TCreature::GetHealth(void){
|
||||
int MaxHitPoints = this->Skills[SKILL_HITPOINTS]->Max;
|
||||
if(MaxHitPoints <= 0){
|
||||
if(!this->IsDead){
|
||||
error("TCreature::GetHealth: MaxHitpoints von %s ist %d, obwohl sie nicht tot ist.\n",
|
||||
this->Name, MaxHitPoints);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CurrentHitPoints = this->Skills[SKILL_HITPOINTS]->Get();
|
||||
int Health = CurrentHitPoints * 100 / MaxHitPoints;
|
||||
if(Health <= 0){
|
||||
Health = (int)(CurrentHitPoints != 0);
|
||||
}
|
||||
return Health;
|
||||
}
|
||||
|
||||
int TCreature::GetSpeed(void){
|
||||
TSkill *GoStrength = this->Skills[SKILL_GO_STRENGTH];
|
||||
if(GoStrength == NULL){
|
||||
error("TCreature::GetSpeed: Kein Skill GOSTRENGTH vorhanden.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return GoStrength->Get() * 2 + 80;
|
||||
}
|
||||
|
||||
int TCreature::Damage(TCreature *Attacker, int Damage, int DamageType){
|
||||
@ -462,13 +741,143 @@ int TCreature::Damage(TCreature *Attacker, int Damage, int DamageType){
|
||||
return Damage;
|
||||
}
|
||||
|
||||
void TCreature::Death(void){
|
||||
this->IsDead = true;
|
||||
this->LoggingOut = true;
|
||||
}
|
||||
|
||||
bool TCreature::MovePossible(int x, int y, int z, bool Execute, bool Jump){
|
||||
bool Result;
|
||||
|
||||
if(Jump){
|
||||
Result = JumpPossible(x, y, z, false);
|
||||
}else{
|
||||
Result = CoordinateFlag(x, y, z, BANK)
|
||||
&& !CoordinateFlag(x, y, z, UNPASS);
|
||||
}
|
||||
|
||||
if(Result && !Execute && CoordinateFlag(x, y, z, AVOID)){
|
||||
Result = false;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool TCreature::IsPeaceful(void){
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 TCreature::GetMaster(void){
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TCreature::TalkStimulus(uint32 SpeakerID, const char *Text){
|
||||
// no-op
|
||||
}
|
||||
|
||||
void TCreature::DamageStimulus(uint32 AttackerID, int Damage, int DamageType){
|
||||
// no-op
|
||||
}
|
||||
|
||||
void TCreature::IdleStimulus(void){
|
||||
// no-op
|
||||
}
|
||||
|
||||
void TCreature::CreatureMoveStimulus(uint32 CreatureID, int Type){
|
||||
if(CreatureID == 0 || CreatureID == this->ID
|
||||
|| this->IsDead
|
||||
|| this->Combat.AttackDest != CreatureID
|
||||
|| this->Combat.ChaseMode != CHASE_MODE_CLOSE
|
||||
|| this->Combat.EarliestAttackTime <= (ServerMilliseconds + 200)){
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(fusion): Find out what `Type` is here.
|
||||
if(Type != 2 // STIMULUS_TYPE??
|
||||
|| !this->LockToDo
|
||||
|| this->ActToDo >= this->NrToDo
|
||||
|| this->ToDoList.at(this->ActToDo)->Code != TDAttack){
|
||||
return;
|
||||
}
|
||||
|
||||
TCreature *Target = GetCreature(this->Combat.AttackDest);
|
||||
if(Target == NULL){
|
||||
return;
|
||||
}
|
||||
|
||||
int Distance = ObjectDistance(this->CrObject, Target->CrObject);
|
||||
if(Distance <= 1){
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(fusion): Review this.
|
||||
try{
|
||||
if(this->ToDoClear() && this->Type == PLAYER){
|
||||
SendSnapback(this->Connection);
|
||||
}
|
||||
this->ToDoWait(200);
|
||||
this->ToDoAttack();
|
||||
this->ToDoStart();
|
||||
}catch(RESULT r){
|
||||
if(this->Type == PLAYER){
|
||||
SendResult(this->Connection, r);
|
||||
}
|
||||
this->ToDoClear();
|
||||
this->ToDoWait(this->Combat.EarliestAttackTime);
|
||||
this->ToDoStart();
|
||||
}
|
||||
}
|
||||
|
||||
void TCreature::AttackStimulus(uint32 AttackerID){
|
||||
// no-op
|
||||
}
|
||||
|
||||
// Creature Management
|
||||
// =============================================================================
|
||||
bool IsCreaturePlayer(uint32 CreatureID){
|
||||
return CreatureID < 0x40000000;
|
||||
}
|
||||
|
||||
//
|
||||
TCreature *GetCreature(uint32 CreatureID){
|
||||
if(CreatureID == 0){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TCreature *Creature = HashList[CreatureID % NARRAY(HashList)];
|
||||
while(Creature != NULL && Creature->ID != CreatureID){
|
||||
Creature = Creature->NextHashEntry;
|
||||
}
|
||||
|
||||
return Creature;
|
||||
}
|
||||
|
||||
TCreature *GetCreature(Object Obj){
|
||||
return GetCreature(Obj.getCreatureID());
|
||||
}
|
||||
|
||||
void InsertChainCreature(TCreature *Creature, int CoordX, int CoordY){
|
||||
if(Creature == NULL){
|
||||
// TODO(fusion): Maybe a typo on the name of the function? I thought it
|
||||
// could be some type of macro because there was no function name mismatch
|
||||
// until now.
|
||||
error("DeleteChainCreature: Übegebene Kreatur existiert nicht.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(CoordX == 0){
|
||||
CoordX = Creature->posx;
|
||||
}
|
||||
|
||||
if(CoordY == 0){
|
||||
CoordY = Creature->posy;
|
||||
}
|
||||
|
||||
int ChainX = CoordX / 16;
|
||||
int ChainY = CoordY / 16;
|
||||
uint32 *FirstID = FirstChainCreature->at(ChainX, ChainY);
|
||||
Creature->NextChainCreature = *FirstID;
|
||||
*FirstID = Creature->ID;
|
||||
}
|
||||
|
||||
void DeleteChainCreature(TCreature *Creature){
|
||||
if(Creature == NULL){
|
||||
@ -479,9 +888,9 @@ void DeleteChainCreature(TCreature *Creature){
|
||||
// NOTE(fusion): All creatures in each 16x16 region form a creature linked
|
||||
// list, despite its current floor. Whether that is a good idea is a whole
|
||||
// other matter.
|
||||
int BlockX = Creature->posx / 16;
|
||||
int BlockY = Creature->posy / 16;
|
||||
uint32 *FirstID = FirstChainCreature->at(BlockX, BlockY);
|
||||
int ChainX = Creature->posx / 16;
|
||||
int ChainY = Creature->posy / 16;
|
||||
uint32 *FirstID = FirstChainCreature->at(ChainX, ChainY);
|
||||
|
||||
if(*FirstID == Creature->ID){
|
||||
*FirstID = Creature->NextChainCreature;
|
||||
@ -509,8 +918,112 @@ void DeleteChainCreature(TCreature *Creature){
|
||||
}
|
||||
}
|
||||
|
||||
void MoveChainCreature(TCreature *Creature, int CoordX, int CoordY){
|
||||
if(Creature == NULL){
|
||||
error("DeleteChainCreature: Übegebene Kreatur existiert nicht.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int NewChainX = CoordX / 16;
|
||||
int NewChainY = CoordY / 16;
|
||||
int OldChainX = Creature->posx / 16;
|
||||
int OldChainY = Creature->posy / 16;
|
||||
|
||||
if(NewChainX != OldChainX || NewChainY != OldChainY){
|
||||
DeleteChainCreature(Creature);
|
||||
InsertChainCreature(Creature, CoordX, CoordY);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessCreatures(void){
|
||||
for(int Index = 0; Index < FirstFreeCreature; Index += 1){
|
||||
TCreature *Creature = *CreatureList.at(Index);
|
||||
if(Creature == NULL){
|
||||
error("ProcessCreatures: Kreatur %d existiert nicht.\n", Index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO(fusion): It is weird that we do check the connection all the time
|
||||
// and most of the time it is redundant because functions will check if
|
||||
// it's NULL before attempting anything.
|
||||
|
||||
int FoodRegen = Creature->Skills[SKILL_FED]->Get();
|
||||
if(FoodRegen > 0 && (RoundNr % FoodRegen) == 0 && !Creature->IsDead
|
||||
&& !IsProtectionZone(Creature->posx, Creature->posy, Creature->posz)){
|
||||
Creature->Skills[SKILL_HITPOINTS]->Change(1);
|
||||
Creature->Skills[SKILL_MANA]->Change(4);
|
||||
if(Creature->Type == PLAYER){
|
||||
SendPlayerData(Creature->Connection);
|
||||
}
|
||||
}
|
||||
|
||||
if(Creature->Type == PLAYER){
|
||||
if(Creature->Connection != NULL){
|
||||
((TPlayer*)Creature)->CheckState();
|
||||
}
|
||||
|
||||
if(Creature->EarliestLogoutRound != 0 && Creature->EarliestLogoutRound <= RoundNr){
|
||||
((TPlayer*)Creature)->ClearPlayerkillingMarks();
|
||||
Creature->EarliestLogoutRound = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(!Creature->IsDead && Creature->Skills[SKILL_HITPOINTS]->Get() <= 0){
|
||||
error("ProcessCreatures: Kreatur %s ist nicht tot, obwohl sie keine HP mehr hat.\n", Creature->Name);
|
||||
Creature->Death();
|
||||
}
|
||||
|
||||
if(Creature->LoggingOut && Creature->LogoutPossible() == 0){ // LOGOUT_POSSIBLE ?
|
||||
if(Creature->IsDead && Creature->Skills[SKILL_HITPOINTS]->Get() > 0){
|
||||
error("ProcessCreatures: Kreatur %s hat HP, obwohl sie tot ist.\n", Creature->Name);
|
||||
Creature->Skills[SKILL_HITPOINTS]->Set(0);
|
||||
}
|
||||
|
||||
// TODO(fusion): Creatures are removed from `CreatureList` with a swap
|
||||
// and pop. Since we're iterating it RIGHT NOW, we need to process the
|
||||
// the current index AGAIN because it'll now contain the creature that
|
||||
// was previously at the end of the list. The annoying part here is that
|
||||
// this removal occurs implicitly in the creature's destructor.
|
||||
delete Creature;
|
||||
Index -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessSkills(void){
|
||||
for(int Index = 0; Index < FirstFreeCreature; Index += 1){
|
||||
TCreature *Creature = *CreatureList.at(Index);
|
||||
if(Creature == NULL){
|
||||
error("ProcessSkills: Kreatur %d existiert nicht.\n", Index);
|
||||
continue;
|
||||
}
|
||||
|
||||
Creature->ProcessSkills();
|
||||
}
|
||||
}
|
||||
|
||||
void MoveCreatures(int Delay){
|
||||
ServerMilliseconds += Delay;
|
||||
while(ToDoQueue.Entries > 0){
|
||||
auto Entry = *ToDoQueue.Entry->at(1);
|
||||
uint32 ExecutionTime = Entry.Key;
|
||||
uint32 CreatureID = Entry.Data;
|
||||
if(ExecutionTime > ServerMilliseconds){
|
||||
break;
|
||||
}
|
||||
|
||||
ToDoQueue.deleteMin();
|
||||
TCreature *Creature = GetCreature(CreatureID);
|
||||
if(Creature != NULL){
|
||||
Creature->Execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kill Statistics
|
||||
// =============================================================================
|
||||
void InitKillStatistics(void);//TODO
|
||||
void ExitKillStatistics(void);//TODO
|
||||
void AddKillStatistics(int AttackerRace, int DefenderRace){
|
||||
// NOTE(fusion): I think the race name can be "human" only for players,
|
||||
// which means we're probably tracking how many creatures are killed by
|
||||
@ -572,9 +1085,13 @@ TRaceData::TRaceData(void) :
|
||||
this->Spells = 0;
|
||||
}
|
||||
|
||||
bool IsRaceValid(int Race){
|
||||
return Race >= 1 && Race < MAX_RACES;
|
||||
}
|
||||
|
||||
int GetRaceByName(const char *RaceName){
|
||||
int Result = 0;
|
||||
for(int Race = 1; Race < NARRAY(RaceData); Race += 1){
|
||||
for(int Race = 1; Race < MAX_RACES; Race += 1){
|
||||
if(stricmp(RaceName, RaceData[Race].Name) == 0){
|
||||
Result = Race;
|
||||
break;
|
||||
@ -583,7 +1100,35 @@ int GetRaceByName(const char *RaceName){
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Raid
|
||||
void LoadRaces(void); //TODO
|
||||
|
||||
// Monster Raid
|
||||
// =============================================================================
|
||||
void LoadMonsterRaids(void); //TODO
|
||||
|
||||
// Initialization
|
||||
// =============================================================================
|
||||
void InitCr(void){
|
||||
NextCreatureID = 0x40000000;
|
||||
FirstFreeCreature = 0;
|
||||
FirstChainCreature = new matrix<uint32>(
|
||||
SectorXMin * 2, SectorXMax * 2 + 1,
|
||||
SectorYMin * 2, SectorYMax * 2 + 1,
|
||||
0);
|
||||
|
||||
LoadRaces();
|
||||
LoadMonsterRaids();
|
||||
InitCrskill();
|
||||
InitPlayer();
|
||||
InitNonplayer();
|
||||
InitKillStatistics();
|
||||
}
|
||||
|
||||
void ExitCr(void){
|
||||
ExitKillStatistics();
|
||||
ExitPlayer();
|
||||
ExitNonplayer();
|
||||
ExitCrskill();
|
||||
|
||||
delete FirstChainCreature;
|
||||
}
|
||||
|
||||
@ -1159,7 +1159,7 @@ bool TSkillBase::NewSkill(uint16 SkillNo, TCreature *Creature){
|
||||
}
|
||||
|
||||
bool TSkillBase::SetSkills(int Race){
|
||||
if(Race < 0 || Race >= NARRAY(RaceData)){
|
||||
if(!IsRaceValid(Race)){
|
||||
error("TSkillBase::SetSkills: Ungültige Rassennummer %d.\n", Race);
|
||||
return false;
|
||||
}
|
||||
@ -1250,3 +1250,13 @@ void TSkillBase::DelTimer(uint16 SkNr){
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialization
|
||||
//==============================================================================
|
||||
void InitCrskill(void){
|
||||
// no-op
|
||||
}
|
||||
|
||||
void ExitCrskill(void){
|
||||
// no-op
|
||||
}
|
||||
|
||||
@ -372,8 +372,7 @@ TSummonImpact::TSummonImpact(TCreature *Actor, int Race, int Maximum){
|
||||
error("TSummonImpact::TSummonImpact: Actor ist NULL.\n");
|
||||
}
|
||||
|
||||
// TODO(fusion): I think there might be a `IsRaceValid` function that was inlined.
|
||||
if(Race < 0 || Race >= NARRAY(RaceData)){
|
||||
if(!IsRaceValid(Race)){
|
||||
error("TSummonImpact::TSummonImpact: Ungültige Rassennummer %d.\n", Race);
|
||||
}
|
||||
|
||||
@ -387,7 +386,7 @@ void TSummonImpact::handleField(int x, int y, int z){
|
||||
int Race = this->Race;
|
||||
int Maximum = this->Maximum;
|
||||
if(Actor != NULL
|
||||
&& Race >= 0 && Race < NARRAY(RaceData)
|
||||
&& IsRaceValid(Race)
|
||||
&& Actor->SummonedCreatures < Maximum){
|
||||
int x, y, z;
|
||||
if(SearchSummonField(&x, &y, &z, 2)){
|
||||
@ -1870,7 +1869,7 @@ void SummonCreature(TCreature *Actor, int ManaPoints, int Race, bool God){
|
||||
throw ERROR;
|
||||
}
|
||||
|
||||
if(Race < 1 || Race >= NARRAY(RaceData)){
|
||||
if(!IsRaceValid(Race)){
|
||||
error("SummonCreature: Ungültige Rassennummer %d übergeben.\n", Race);
|
||||
throw ERROR;
|
||||
}
|
||||
|
||||
28
src/map.cc
28
src/map.cc
@ -8,21 +8,21 @@
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
static int OBCount;
|
||||
static int SectorXMin;
|
||||
static int SectorXMax;
|
||||
static int SectorYMin;
|
||||
static int SectorYMax;
|
||||
static int SectorZMin;
|
||||
static int SectorZMax;
|
||||
static int RefreshedCylinders;
|
||||
static int NewbieStartPositionX;
|
||||
static int NewbieStartPositionY;
|
||||
static int NewbieStartPositionZ;
|
||||
static int VeteranStartPositionX;
|
||||
static int VeteranStartPositionY;
|
||||
static int VeteranStartPositionZ;
|
||||
int SectorXMin;
|
||||
int SectorXMax;
|
||||
int SectorYMin;
|
||||
int SectorYMax;
|
||||
int SectorZMin;
|
||||
int SectorZMax;
|
||||
int RefreshedCylinders;
|
||||
int NewbieStartPositionX;
|
||||
int NewbieStartPositionY;
|
||||
int NewbieStartPositionZ;
|
||||
int VeteranStartPositionX;
|
||||
int VeteranStartPositionY;
|
||||
int VeteranStartPositionZ;
|
||||
|
||||
static int OBCount;
|
||||
static matrix3d<TSector*> *Sector;
|
||||
static TObjectBlock **ObjectBlock;
|
||||
static TObject *FirstFreeObject;
|
||||
|
||||
15
src/map.hh
15
src/map.hh
@ -97,6 +97,21 @@ struct TCronEntry {
|
||||
int Next;
|
||||
};
|
||||
|
||||
// NOTE(fusion): Map config values.
|
||||
extern int SectorXMin;
|
||||
extern int SectorXMax;
|
||||
extern int SectorYMin;
|
||||
extern int SectorYMax;
|
||||
extern int SectorZMin;
|
||||
extern int SectorZMax;
|
||||
extern int RefreshedCylinders;
|
||||
extern int NewbieStartPositionX;
|
||||
extern int NewbieStartPositionY;
|
||||
extern int NewbieStartPositionZ;
|
||||
extern int VeteranStartPositionX;
|
||||
extern int VeteranStartPositionY;
|
||||
extern int VeteranStartPositionZ;
|
||||
|
||||
// NOTE(fusion): Cron management functions. Most for internal use.
|
||||
Object CronCheck(void);
|
||||
void CronExpire(Object Obj, int Delay);
|
||||
|
||||
12
src/stubs.hh
12
src/stubs.hh
@ -18,6 +18,7 @@ extern void AbortWriter(void);
|
||||
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);
|
||||
extern void ChangeNPCState(TCreature *Npc, int NewState, bool Stimulus);
|
||||
extern void CharacterDeathOrder(TCreature *Creature, int OldLevel,
|
||||
uint32 Offender, const char *Remark, bool Unjustified);
|
||||
extern bool CheckRight(uint32 CreatureID, RIGHT Right);
|
||||
@ -33,11 +34,11 @@ extern void CreatePool(Object Con, ObjectType Type, uint32 Value);
|
||||
extern void Delete(Object Obj, int Count);
|
||||
extern bool FieldPossible(int x, int y, int z, int FieldType);
|
||||
extern Object GetBodyObject(uint32 CreatureID, int Position);
|
||||
extern TCreature *GetCreature(uint32 CreatureID);
|
||||
extern TCreature *GetCreature(Object Obj);
|
||||
extern void GetExitPosition(uint16 HouseID, int *x, int *y, int *z);
|
||||
extern TConnection *GetFirstConnection(void);
|
||||
extern TConnection *GetNextConnection(void);
|
||||
extern const char *GetName(Object Obj);
|
||||
extern Object GetObject(uint32 CreatureID, int x, int y, int z, int RNum, ObjectType Type);
|
||||
extern TPlayer *GetPlayer(uint32 CreatureID);
|
||||
extern bool GetRaceNoConvince(int Race);
|
||||
extern bool GetRaceNoIllusion(int Race);
|
||||
@ -51,25 +52,24 @@ extern void GraphicalEffect(Object Obj, int Type);
|
||||
extern int IdentifyPlayer(const char *Name, bool ExactMatch, bool IgnoreGamemasters, TPlayer **Player);
|
||||
extern void InitLog(const char *ProtocolName);
|
||||
extern void KickGuest(uint16 HouseID, TPlayer *Host, TPlayer *Guest);
|
||||
extern bool LagDetected(void);
|
||||
extern void LoadMonsterRaid(const char *FileName, int Start,
|
||||
bool *Type, int *Date, int *Interval, int *Duration);
|
||||
extern void Log(const char *ProtocolName, const char *Text, ...) ATTR_PRINTF(2, 3);
|
||||
extern void LogoutAllPlayers(void);
|
||||
extern void Missile(Object Start, Object Dest, int Type);
|
||||
extern void Move(uint32 CreatureID, Object Obj, Object Con, int Count, bool NoMerge, Object Ignore);
|
||||
extern void MoveCreatures(int Delay);
|
||||
extern void NetLoadCheck(void);
|
||||
extern void NetLoadSummary(void);
|
||||
extern void NotifyAllCreatures(Object Obj, int Type, Object OldCon);
|
||||
extern int ObjectDistance(Object Obj1, Object Obj2);
|
||||
extern bool ObjectInRange(uint32 CreatureID, Object Obj, int Range);
|
||||
extern void ProcessCommunicationControl(void);
|
||||
extern void ProcessConnections(void);
|
||||
extern void ProcessCreatures(void);
|
||||
extern void ProcessCronSystem(void);
|
||||
extern void ProcessMonsterhomes(void);
|
||||
extern void ProcessMonsterRaids(void);
|
||||
extern void ProcessReaderThreadReplies(TRefreshSectorFunction *RefreshSector, TSendMailsFunction *SendMails);
|
||||
extern void ProcessSkills(void);
|
||||
extern void ProcessWriterThreadReplies(void);
|
||||
extern void ReceiveData(void);
|
||||
extern void RefreshCylinders(void);
|
||||
@ -77,6 +77,7 @@ extern void RefreshMap(void);
|
||||
extern void RefreshSector(int SectorX, int SectorY, int SectorZ, const uint8 *Data, int Size);
|
||||
extern void SavePlayerDataOrder(void);
|
||||
extern bool SearchFlightField(uint32 FugitiveID, uint32 PursuerID, int *x, int *y, int *z);
|
||||
extern bool SearchLoginField(int *x, int *y, int *z, int Distance, bool Player);
|
||||
extern bool SearchSummonField(int *x, int *y, int *z, int Distance);
|
||||
extern void SendAll(void);
|
||||
extern void SendAmbiente(TConnection *Connection);
|
||||
@ -89,6 +90,7 @@ extern void SendPlayerData(TConnection *Connection);
|
||||
extern void SendPlayerSkills(TConnection *Connection);
|
||||
extern void SendPlayerState(TConnection *Connection, uint8 State);
|
||||
extern void SendResult(TConnection *Connection, RESULT r);
|
||||
extern void SendSnapback(TConnection *Connection);
|
||||
extern void ShowGuestList(uint16 HouseID, TPlayer *Player, char *Buffer);
|
||||
extern void ShowSubownerList(uint16 HouseID, TPlayer *Player, char *Buffer);
|
||||
extern void ShowNameDoor(Object Door, TPlayer *Player, char *Buffer);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user