Last active
August 28, 2018 03:03
-
-
Save wallabra/2bead16a3c1e4da8f281affc6ded2d07 to your computer and use it in GitHub Desktop.
Simple C++ actor system.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * GAS - the Gustavo Actor System | |
| * (with Event System included) | |
| * | |
| * Author: Gustavo Ramos "Gustavo6046" Rehermann. | |
| * (c)2018. The MIT License. | |
| * | |
| * This system basically allows the creation of | |
| * Actors (entities), in a game world. This does | |
| * not handle 3D coordinates nor collision. I | |
| * have neither ideas nor time to implement those, | |
| * so I hope someone will implement those on top | |
| * of this code, either extending it or modifying it. | |
| * | |
| * For the sake of extensibility, actors aren't removed | |
| * when they die. That task is here delegated to the | |
| * upper levels. | |
| * | |
| * For an example of a possible extension: | |
| * | |
| * struct Vec3 | |
| * { | |
| * int XYZ[3]; | |
| * } | |
| * | |
| * struct G_PhysicalActor | |
| * { | |
| * G_Actor* baseActor; | |
| * Vec3 pos; | |
| * int colCylinderRadius; | |
| * int colCylinderHeight; | |
| * // ... | |
| * } | |
| */ | |
| #include <iostream> | |
| #include <cmath> | |
| #include <algorithm> | |
| #include <cstring> | |
| #include <cstdlib> | |
| #include "actor.h" // > structs | |
| E_ActorEvent* E_BaseEvent = NULL; // > Master actor event linked list. | |
| E_ActorEvent* E_DeinitEvent = NULL; // > List of events to be deallocated on exit. | |
| G_Actor* G_Actors = NULL; // > Master actor linked list. | |
| unsigned int G_NumActors = 0; | |
| // > This function shall append actor events | |
| // to the master event linked list (I call | |
| // this event notification). | |
| void G_NotifyActorEvent(E_ActorEvent* evt) | |
| { | |
| evt->nextEvent = NULL; | |
| if ( E_BaseEvent == NULL ) | |
| E_BaseEvent = evt; | |
| else | |
| { | |
| E_ActorEvent* e = NULL; | |
| for ( e = E_BaseEvent; e->nextEvent != NULL; e = e->nextEvent ); // > quick way to get | |
| // the last event on | |
| // the linked list :) | |
| e->nextEvent = evt; | |
| } | |
| } | |
| // > Returns the next actor event. | |
| E_ActorEvent* G_PollActorEvent() | |
| { | |
| // > For the sake of while loop polling, we want | |
| // to return NULL (0) when there is no event. | |
| if ( !E_BaseEvent ) return NULL; | |
| // > Since our linked list is like a queue, we want | |
| // to take only the 1st event (FIFO), and the 2nd | |
| // will become the 1st. Fortunately, this is very | |
| // easy to do. | |
| E_ActorEvent* res = E_BaseEvent; | |
| E_BaseEvent = res->nextEvent; | |
| res->nextEvent = E_DeinitEvent; | |
| E_DeinitEvent = res; | |
| return res; | |
| } | |
| // > Handles deinitialization and deallocation. | |
| void M_Deinit() | |
| { | |
| E_ActorEvent* e; | |
| while ( E_DeinitEvent != NULL ) | |
| { | |
| e = E_DeinitEvent; | |
| E_DeinitEvent = E_DeinitEvent->nextEvent; | |
| delete e; | |
| } | |
| G_Actor* a; | |
| while ( G_Actors != NULL ) | |
| { | |
| a = G_Actors; | |
| G_Actors = a->nextActor; | |
| delete a; | |
| } | |
| } | |
| // > Handle actor death (health being | |
| // smaller than or equal to 0). | |
| void G_Die(G_Actor* actor) | |
| { | |
| // > At the moment, all it does is notify an event. | |
| // This may change. | |
| E_ActorEvent* evt = new E_ActorEvent; | |
| evt->eventType = AEV_DEATH; | |
| evt->actor = actor; | |
| G_NotifyActorEvent(evt); | |
| } | |
| // > Create an actor and append it to | |
| // the master Actor linked list. | |
| G_Actor* G_CreateActor(int health) | |
| { | |
| // > Initializes the actor. | |
| G_Actor* a = new G_Actor; | |
| _G_DamageTrack* dmg = new _G_DamageTrack; | |
| dmg->health = health; | |
| dmg->totalDamage = 0; | |
| dmg->totalHeal = 0; | |
| a->damage = *dmg; | |
| a->index = G_NumActors; | |
| delete dmg; | |
| char* estr = new char[1]; // allocate an empty string. | |
| *estr = 0; // strings terminate with a null byte. | |
| std::fill_n(a->damageFactor_K, 256, estr); | |
| std::fill_n(a->damageFactor_V, 256, 1000); | |
| // > Adds the actor to the list. | |
| if ( G_Actors == NULL ) | |
| G_Actors = a; | |
| else | |
| { | |
| // > Find last element on actor list, then append | |
| // to it. | |
| G_Actor* prev; | |
| for ( prev = G_Actors; prev->nextActor != NULL; prev = prev->nextActor ); | |
| prev->nextActor = a; | |
| } | |
| // > Notify the actor's creation. | |
| G_NumActors++; | |
| E_ActorEvent* evt = new E_ActorEvent; | |
| evt->eventType = AEV_CREATED; | |
| evt->actor = a; | |
| G_NotifyActorEvent(evt); | |
| a->nextActor = NULL; | |
| return a; | |
| } | |
| // > Removes this actor from the list, by address. | |
| bool G_RemoveActor(G_Actor* actor) | |
| { | |
| G_Actor* a = NULL; | |
| if ( G_Actors == actor ) | |
| { | |
| G_Actors = NULL; | |
| return true; | |
| } | |
| if ( G_Actors == NULL ) | |
| return false; | |
| for ( a = G_Actors; a->nextActor != NULL && a->nextActor != actor; a = a->nextActor ) | |
| if ( a == NULL || a->nextActor == NULL ) | |
| return false; | |
| if ( a->nextActor != NULL ) | |
| { | |
| a->nextActor = a->nextActor->nextActor; | |
| if ( a->nextActor != NULL ) | |
| for ( G_Actor* cur = a->nextActor; cur != NULL; cur = cur->nextActor ) | |
| cur->index--; | |
| } | |
| else | |
| a->nextActor = NULL; | |
| E_ActorEvent* evt = new E_ActorEvent; | |
| evt->eventType = AEV_REMOVED; | |
| evt->actor = actor; | |
| G_NotifyActorEvent(evt); // > Notify the actor's removal. | |
| return true; | |
| } | |
| // > Perform damage checks on the actor pointed. If | |
| // the actor's health is smaller than 1 (it's not | |
| // a floating point number, so we can safely assume | |
| // that i < 1 is the same as i <= 0), we call the die | |
| // routine on it. | |
| // | |
| // > For the sake of modifiability, this function exists; | |
| // however, it is only an if statement with a call to | |
| // G_Die. | |
| void G_DamageCheck(G_Actor* actor) | |
| { | |
| if ( actor->damage.health < 1 ) | |
| G_Die(actor); | |
| } | |
| // > Deal damage to an actor, and handle damage factors | |
| // (unless specified otherwise). Automatically calls | |
| // the damage check function (kills actor if health is | |
| // null or negative). | |
| void G_TakeDamage(G_Actor* actor, char* damageType, int damage, bool checkFactors) | |
| { | |
| // > Apply damage factors. | |
| if ( checkFactors ) | |
| for ( unsigned int i = 0; i < 256 && std::strlen(actor->damageFactor_K[i]) > 0; i++ ) | |
| if ( std::strcmp(actor->damageFactor_K[i], damageType) == 0 ) | |
| damage = damage * actor->damageFactor_V[i] / 1000; | |
| actor->damage.health -= damage; | |
| // > Damage shall be tracked by other _G_DamageTrack | |
| // members as well! | |
| if ( damage < 0 ) | |
| actor->damage.totalHeal -= damage; | |
| else | |
| actor->damage.totalDamage += damage; | |
| if ( damage > 0 ) | |
| { | |
| E_ActorEvent* evt1 = new E_ActorEvent; | |
| evt1->eventType = AEV_DAMAGE; | |
| evt1->eventData = damage; | |
| evt1->actor = actor; | |
| G_NotifyActorEvent(evt1); // > Notify the actor's damage. | |
| } | |
| else | |
| { | |
| E_ActorEvent* evt1 = new E_ActorEvent; | |
| evt1->eventType = AEV_HEAL; | |
| evt1->eventData = -damage; | |
| evt1->actor = actor; | |
| G_NotifyActorEvent(evt1); // > Notify the actor's heal. | |
| } | |
| E_ActorEvent* evt1 = new E_ActorEvent; | |
| evt1->eventType = AEV_MODHEALTH; | |
| evt1->eventData = -damage; | |
| evt1->actor = actor; | |
| G_NotifyActorEvent(evt1); // > Notify any modification to the | |
| // actor's physical integrity. | |
| G_DamageCheck(actor); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef GUSTAVO_ACTORSYS_HEADER | |
| #define GUSTAVO_ACTORSYS_HEADER | |
| /* | |
| * Actor System (header) | |
| * (with Event System included) | |
| * | |
| * Author: Gustavo Ramos "Gustavo6046" Rehermann. | |
| * (c)2018. The MIT License. | |
| * | |
| * This system basically allows the creation of | |
| * Actors (entities), in a game world. This does | |
| * not handle 3D coordinates nor collision. I | |
| * have neither ideas nor time to implement those, | |
| * so I hope someone will implement those on top | |
| * of this code, either extending it or modifying it. | |
| * | |
| * For the sake of extensibility, actors aren't removed | |
| * when they die. That task is here delegated to the | |
| * upper levels. | |
| * | |
| * For an example of a possible extension: | |
| * | |
| * struct Vec3 | |
| * { | |
| * int XYZ[3]; | |
| * } | |
| * | |
| * struct G_PhysicalActor | |
| * { | |
| * G_Actor* baseActor; | |
| * Vec3 pos; | |
| * int colCylinderRadius; | |
| * int colCylinderHeight; | |
| * // ... | |
| * } | |
| */ | |
| #define AEV_DEATH 0 // > Surprise! This actor died! | |
| #define AEV_DAMAGE 1 // > This actor took positive damage. | |
| #define AEV_HEAL 2 // > This actor took negative damage (heal). | |
| #define AEV_MODHEALTH 3 // > This actor's health was modified (by | |
| // the G_TakeDamage function, most | |
| // likely). | |
| #define AEV_CREATED 4 // > This actor was newly created. | |
| #define AEV_REMOVED 5 // > This actor was removed from the master linked list.. | |
| // > This structure will track the physical integrity | |
| // of an actor. It is supported by standard functions, | |
| // like G_TakeDamage. | |
| struct _G_DamageTrack | |
| { | |
| int health; // > tracked actor's health | |
| unsigned int totalDamage; // > tracked actor's total damage (scaled | |
| // by damage factors) | |
| unsigned int totalHeal; // > tracked actor's total heal (negative damage) | |
| }; | |
| struct G_Actor | |
| { | |
| // char* name; // > actor's human name | |
| // char* id; // > actor's unique identifier | |
| unsigned int index; // > actor's G_Actors index + 1. | |
| // If this is equal to 0, this actor | |
| // should be assumed not to be in | |
| // G_Actors. | |
| _G_DamageTrack damage; // > keeps track of 'health' (physical | |
| // integrity) and damage | |
| char* damageFactor_K[256]; // > this actor's damage factor map's keys | |
| int damageFactor_V[256]; // > this actor's damage factor map's values | |
| G_Actor* nextActor; // > actors follow a linked list. | |
| }; | |
| struct E_ActorEvent | |
| { | |
| unsigned char eventType; // > event's type (as defined in AEV_* constants) | |
| int eventData; // > extra information about the event | |
| G_Actor* actor; // > pointer to event's reference actor | |
| E_ActorEvent* nextEvent; // > events, like actors, follow a linked list. | |
| }; | |
| extern E_ActorEvent* E_BaseEvent; // > Master actor event linked list. | |
| extern G_Actor* G_Actors; // > Master actor linked list. | |
| extern unsigned int G_NumActors; | |
| // > This function shall append actor events | |
| // to the master event linked list (I call | |
| // this event notification). | |
| extern void G_NotifyActorEvent(E_ActorEvent* evt); | |
| // > Returns the next actor event. | |
| // | |
| extern E_ActorEvent* G_PollActorEvent(); | |
| // > Handle actor death (health being | |
| // smaller than or equal to 0). | |
| extern void G_Die(G_Actor* actor); | |
| // > Create an actor and append it to | |
| // the master Actor linked list. | |
| extern G_Actor* G_CreateActor(int health); | |
| // > Handles deinitialization and deallocation. | |
| void M_Deinit(); | |
| // > Perform damage checks on the actor pointed. If | |
| // the actor's health is smaller than 1 (it's not | |
| // a floating point number, so we can safely assume | |
| // that i < 1 is the same as i <= 0), we call the die | |
| // routine on it. | |
| extern void G_DamageCheck(G_Actor* actor); | |
| // > Deal damage to an actor, and handle damage factors | |
| // (unless specified otherwise). Automatically calls | |
| // the damage check function (kills actor if health is | |
| // null or negative). | |
| extern void G_TakeDamage(G_Actor* actor, char* damageType, int damage, bool checkFactors = true); | |
| // > Removes this actor from the list, by address. | |
| extern bool G_RemoveActor(G_Actor* actor); | |
| #endif /* GUSTAVO_ACTORSYS_HEADER */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Actor system usage example. | |
| * | |
| * Author: Gustavo Ramos "Gustavo6046" Rehermann. | |
| * (c)2018. The MIT License. | |
| */ | |
| #include <iostream> | |
| #include "actor.h" | |
| int main() | |
| { | |
| G_Actor* myActor = G_CreateActor(80); | |
| G_Actor* myActor2 = G_CreateActor(200); | |
| myActor2->damageFactor_K[0] = (char*) "fire"; // Let's just say it's a dragon! | |
| myActor2->damageFactor_V[0] = 100; | |
| myActor2->damageFactor_K[1] = (char*) "default"; // And it has hard scales. | |
| myActor2->damageFactor_V[1] = 575; | |
| myActor2->damageFactor_K[2] = (char*) "water"; // But it hates water! | |
| myActor2->damageFactor_V[2] = 2420; | |
| std::cout << "Actor 0 address: " << myActor << "\n"; | |
| std::cout << "Actor 1 address: " << myActor2 << "\n"; | |
| std::cout << "Actor 0's initial health: " << myActor->damage.health << "\n"; | |
| std::cout << "Actor 1's initial health: " << myActor2->damage.health << "\n"; | |
| G_TakeDamage(myActor, (char*) "default", 30); | |
| G_TakeDamage(myActor, (char*) "default", 40); | |
| G_TakeDamage(myActor, (char*) "default", -30); | |
| G_TakeDamage(myActor, (char*) "default", 100); | |
| G_TakeDamage(myActor2, (char*) "fire", 314); | |
| G_TakeDamage(myActor2, (char*) "default", 120); | |
| G_TakeDamage(myActor2, (char*) "water", 90); | |
| std::cout << "Actor 0's final health: " << myActor->damage.health << "\n"; | |
| std::cout << "Actor 1's final health: " << myActor2->damage.health << "\n"; | |
| G_RemoveActor(myActor); | |
| std::cout << "Actor removed.\n"; | |
| E_ActorEvent* evt; | |
| std::cout << "\n== EVENT LOG ==\n\n"; | |
| // Print every damage/death event notified in the meanwhile. | |
| while ( (evt = G_PollActorEvent()) != NULL ) | |
| { | |
| if ( evt->eventType == AEV_CREATED ) | |
| std::cout << "Actor " << evt->actor->index << " was created!\n"; | |
| if ( evt->eventType == AEV_DAMAGE ) | |
| std::cout << "Actor " << evt->actor->index << " took " << evt->eventData << "hp damage!\n"; | |
| else if ( evt->eventType == AEV_DEATH ) | |
| std::cout << "Actor " << evt->actor->index << " has died!\n"; | |
| else if ( evt->eventType == AEV_HEAL ) | |
| std::cout << "Actor " << evt->actor->index << " healed " << evt->eventData << "hp!\n"; | |
| else if ( evt->eventType == AEV_REMOVED ) | |
| std::cout << "Actor " << evt->actor->index << " was removed from the actor list.\n"; | |
| } | |
| M_Deinit(); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment