mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-03-17 08:21:28 +00:00
Merge branch 'asmjit' of https://github.com/coelckers/gzdoom
This commit is contained in:
commit
17eca07a6f
23 changed files with 90 additions and 175 deletions
|
@ -116,7 +116,7 @@ public:
|
|||
botinfo_t *botinfo;
|
||||
int spawn_tries;
|
||||
int wanted_botnum;
|
||||
TObjPtr<AActor*> firstthing;
|
||||
TObjPtr<AInventory*> firstthing;
|
||||
TObjPtr<AActor*> body1;
|
||||
TObjPtr<AActor*> body2;
|
||||
|
||||
|
|
|
@ -372,7 +372,6 @@ public:
|
|||
player_t &operator= (const player_t &p);
|
||||
|
||||
void Serialize(FSerializer &arc);
|
||||
size_t FixPointers (const DObject *obj, DObject *replacement);
|
||||
size_t PropagateMark();
|
||||
|
||||
void SetLogNumber (int num);
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include "actor.h"
|
||||
#include "doomstat.h" // Ideally, DObjects can be used independant of Doom.
|
||||
#include "d_player.h" // See p_user.cpp to find out why this doesn't work.
|
||||
#include "g_game.h" // Needed for bodyque.
|
||||
#include "c_dispatch.h"
|
||||
#include "dsectoreffect.h"
|
||||
#include "serializer.h"
|
||||
|
@ -307,7 +306,6 @@ DObject::~DObject ()
|
|||
if (!(ObjectFlags & OF_Released))
|
||||
{
|
||||
// Find all pointers that reference this object and NULL them.
|
||||
StaticPointerSubstitution(this, NULL);
|
||||
Release();
|
||||
}
|
||||
}
|
||||
|
@ -478,11 +476,15 @@ size_t DObject::PointerSubstitution (DObject *old, DObject *notOld)
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
// This once was the main method for pointer cleanup, but
|
||||
// nowadays its only use is swapping out PlayerPawns.
|
||||
// This requires pointer fixing throughout all objects and a few
|
||||
// global variables, but it only needs to look at pointers that
|
||||
// can point to a player.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t DObject::StaticPointerSubstitution (DObject *old, DObject *notOld, bool scandefaults)
|
||||
size_t DObject::StaticPointerSubstitution (AActor *old, AActor *notOld)
|
||||
{
|
||||
DObject *probe;
|
||||
size_t changed = 0;
|
||||
|
@ -497,62 +499,29 @@ size_t DObject::StaticPointerSubstitution (DObject *old, DObject *notOld, bool s
|
|||
last = probe;
|
||||
}
|
||||
|
||||
if (scandefaults)
|
||||
{
|
||||
for (auto p : PClassActor::AllActorClasses)
|
||||
{
|
||||
auto def = GetDefaultByType(p);
|
||||
if (def != nullptr)
|
||||
{
|
||||
def->DObject::PointerSubstitution(old, notOld);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go through the bodyque.
|
||||
for (i = 0; i < BODYQUESIZE; ++i)
|
||||
{
|
||||
if (bodyque[i] == old)
|
||||
{
|
||||
bodyque[i] = static_cast<AActor *>(notOld);
|
||||
changed++;
|
||||
}
|
||||
}
|
||||
|
||||
// Go through players.
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i])
|
||||
changed += players[i].FixPointers (old, notOld);
|
||||
}
|
||||
|
||||
for (auto &s : level.sectorPortals)
|
||||
{
|
||||
if (s.mSkybox == old)
|
||||
{
|
||||
s.mSkybox = static_cast<AActor*>(notOld);
|
||||
changed++;
|
||||
APlayerPawn *replacement = static_cast<APlayerPawn *>(notOld);
|
||||
auto &p = players[i];
|
||||
|
||||
if (p.mo == old) p.mo = replacement, changed++;
|
||||
if (p.poisoner.pp == old) p.poisoner = replacement, changed++;
|
||||
if (p.attacker.pp == old) p.attacker = replacement, changed++;
|
||||
if (p.camera.pp == old) p.camera = replacement, changed++;
|
||||
if (p.ConversationNPC.pp == old) p.ConversationNPC = replacement, changed++;
|
||||
if (p.ConversationPC == old) p.ConversationPC = replacement, changed++;
|
||||
}
|
||||
}
|
||||
|
||||
// Go through sectors.
|
||||
for (auto &sec : level.sectors)
|
||||
{
|
||||
#define SECTOR_CHECK(f,t) \
|
||||
if (sec.f.pp == static_cast<t *>(old)) { sec.f = static_cast<t *>(notOld); changed++; }
|
||||
SECTOR_CHECK( SoundTarget, AActor );
|
||||
SECTOR_CHECK( SecActTarget, AActor );
|
||||
SECTOR_CHECK( floordata, DSectorEffect );
|
||||
SECTOR_CHECK( ceilingdata, DSectorEffect );
|
||||
SECTOR_CHECK( lightingdata, DSectorEffect );
|
||||
#undef SECTOR_CHECK
|
||||
if (sec.SoundTarget == old) sec.SoundTarget = notOld;
|
||||
}
|
||||
|
||||
// Go through bot stuff.
|
||||
if (bglobal.firstthing.pp == (AActor *)old) bglobal.firstthing = (AActor *)notOld, ++changed;
|
||||
if (bglobal.body1.pp == (AActor *)old) bglobal.body1 = (AActor *)notOld, ++changed;
|
||||
if (bglobal.body2.pp == (AActor *)old) bglobal.body2 = (AActor *)notOld, ++changed;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
|
|
@ -185,6 +185,8 @@ protected: \
|
|||
|
||||
#include "dobjgc.h"
|
||||
|
||||
class AActor;
|
||||
|
||||
class DObject
|
||||
{
|
||||
public:
|
||||
|
@ -249,11 +251,9 @@ public:
|
|||
inline FString &StringVar(FName field);
|
||||
template<class T> T*& PointerVar(FName field);
|
||||
|
||||
// If you need to replace one object with another and want to
|
||||
// change any pointers from the old object to the new object,
|
||||
// use this method.
|
||||
// This is only needed for swapping out PlayerPawns and absolutely nothing else!
|
||||
virtual size_t PointerSubstitution (DObject *old, DObject *notOld);
|
||||
static size_t StaticPointerSubstitution (DObject *old, DObject *notOld, bool scandefaults = false);
|
||||
static size_t StaticPointerSubstitution (AActor *old, AActor *notOld);
|
||||
|
||||
PClass *GetClass() const
|
||||
{
|
||||
|
|
|
@ -325,15 +325,8 @@ static void MarkRoot()
|
|||
FCanvasTextureInfo::Mark();
|
||||
Mark(E_FirstEventHandler);
|
||||
Mark(E_LastEventHandler);
|
||||
for (auto &s : level.sectorPortals)
|
||||
{
|
||||
Mark(s.mSkybox);
|
||||
}
|
||||
// Mark dead bodies.
|
||||
for (i = 0; i < BODYQUESIZE; ++i)
|
||||
{
|
||||
Mark(bodyque[i]);
|
||||
}
|
||||
level.Mark();
|
||||
|
||||
// Mark players.
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
|
|
|
@ -224,7 +224,10 @@ template<class T,class U> inline T barrier_cast(TObjPtr<U> &o)
|
|||
return static_cast<T>(static_cast<U>(o));
|
||||
}
|
||||
|
||||
template<class T> inline void GC::Mark(TObjPtr<T> &obj)
|
||||
namespace GC
|
||||
{
|
||||
GC::Mark(&obj.o);
|
||||
template<class T> inline void Mark(TObjPtr<T> &obj)
|
||||
{
|
||||
GC::Mark(&obj.o);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -197,8 +197,6 @@ EXTERN_CVAR (Float, mouse_sensitivity)
|
|||
// debug flag to cancel adaptiveness
|
||||
extern bool singletics;
|
||||
|
||||
extern int bodyqueslot;
|
||||
|
||||
|
||||
|
||||
// Needed to store the number of the dummy sky flat.
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
#include "p_spec.h"
|
||||
#include "serializer.h"
|
||||
#include "vm.h"
|
||||
#include "dobjgc.h"
|
||||
|
||||
#include "g_hub.h"
|
||||
#include "g_levellocals.h"
|
||||
|
@ -211,9 +212,6 @@ FString savedescription;
|
|||
// [RH] Name of screenshot file to generate (usually NULL)
|
||||
FString shotfile;
|
||||
|
||||
AActor* bodyque[BODYQUESIZE];
|
||||
int bodyqueslot;
|
||||
|
||||
FString savename;
|
||||
FString BackupSaveName;
|
||||
|
||||
|
@ -1676,13 +1674,14 @@ DEFINE_ACTION_FUNCTION(DObject, G_PickPlayerStart)
|
|||
static void G_QueueBody (AActor *body)
|
||||
{
|
||||
// flush an old corpse if needed
|
||||
int modslot = bodyqueslot%BODYQUESIZE;
|
||||
int modslot = level.bodyqueslot%level.BODYQUESIZE;
|
||||
level.bodyqueslot = modslot + 1;
|
||||
|
||||
if (bodyqueslot >= BODYQUESIZE && bodyque[modslot] != NULL)
|
||||
if (level.bodyqueslot >= level.BODYQUESIZE && level.bodyque[modslot] != NULL)
|
||||
{
|
||||
bodyque[modslot]->Destroy ();
|
||||
level.bodyque[modslot]->Destroy ();
|
||||
}
|
||||
bodyque[modslot] = body;
|
||||
level.bodyque[modslot] = body;
|
||||
|
||||
// Copy the player's translation, so that if they change their color later, only
|
||||
// their current body will change and not all their old corpses.
|
||||
|
@ -1706,7 +1705,6 @@ static void G_QueueBody (AActor *body)
|
|||
body->Scale.Y *= skin.Scale.Y / defaultActor->Scale.Y;
|
||||
}
|
||||
|
||||
bodyqueslot++;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
struct event_t;
|
||||
|
||||
#include "dobjgc.h"
|
||||
|
||||
//
|
||||
// GAME
|
||||
|
@ -94,10 +95,6 @@ void G_AddViewPitch (int look, bool mouse = false);
|
|||
// Adds to consoleplayer's viewangle if allowed
|
||||
void G_AddViewAngle (int yaw, bool mouse = false);
|
||||
|
||||
#define BODYQUESIZE 32
|
||||
class AActor;
|
||||
extern AActor *bodyque[BODYQUESIZE];
|
||||
extern int bodyqueslot;
|
||||
class AInventory;
|
||||
extern const AInventory *SendItemUse, *SendItemDrop;
|
||||
extern int SendItemDropAmount;
|
||||
|
|
|
@ -467,17 +467,6 @@ FState *AWeapon::GetReadyState ()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// AWeapon :: GetStateForButtonName
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FState *AWeapon::GetStateForButtonName (FName button)
|
||||
{
|
||||
return FindState(button);
|
||||
}
|
||||
|
||||
|
||||
/* Weapon slots ***********************************************************/
|
||||
|
||||
|
|
|
@ -132,8 +132,6 @@ public:
|
|||
FState *GetUpState ();
|
||||
FState *GetDownState ();
|
||||
FState *GetReadyState ();
|
||||
|
||||
FState *GetStateForButtonName (FName button);
|
||||
|
||||
enum
|
||||
{
|
||||
|
|
|
@ -1956,6 +1956,24 @@ void FLevelLocals::Tick ()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void FLevelLocals::Mark()
|
||||
{
|
||||
for (auto &s : sectorPortals)
|
||||
{
|
||||
GC::Mark(s.mSkybox);
|
||||
}
|
||||
// Mark dead bodies.
|
||||
for (auto &p : bodyque)
|
||||
{
|
||||
GC::Mark(p);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FLevelLocals::AddScroller (int secnum)
|
||||
{
|
||||
if (secnum < 0)
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
struct FLevelLocals
|
||||
{
|
||||
void Tick ();
|
||||
void Mark();
|
||||
void AddScroller (int secnum);
|
||||
void SetInterMusic(const char *nextmap);
|
||||
void SetMusicVolume(float v);
|
||||
|
@ -85,6 +86,11 @@ struct FLevelLocals
|
|||
TArray<node_t> gamenodes;
|
||||
node_t *headgamenode;
|
||||
TArray<uint8_t> rejectmatrix;
|
||||
|
||||
static const int BODYQUESIZE = 32;
|
||||
TObjPtr<AActor*> bodyque[BODYQUESIZE];
|
||||
int bodyqueslot;
|
||||
|
||||
|
||||
TArray<FSectorPortal> sectorPortals;
|
||||
TArray<FLinePortal> linePortals;
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "serializer.h"
|
||||
#include "doomdata.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "vm.h"
|
||||
|
||||
static double DecalWidth, DecalLeft, DecalRight;
|
||||
static double SpreadZ;
|
||||
|
@ -753,29 +754,20 @@ DBaseDecal *ShootDecal(const FDecalTemplate *tpl, AActor *basisactor, sector_t *
|
|||
return NULL;
|
||||
}
|
||||
|
||||
class ADecal : public AActor
|
||||
DEFINE_ACTION_FUNCTION(ADecal, SpawnDecal)
|
||||
{
|
||||
DECLARE_CLASS (ADecal, AActor);
|
||||
public:
|
||||
void BeginPlay ();
|
||||
};
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
IMPLEMENT_CLASS(ADecal, false, false)
|
||||
|
||||
void ADecal::BeginPlay ()
|
||||
{
|
||||
const FDecalTemplate *tpl = nullptr;
|
||||
|
||||
Super::BeginPlay ();
|
||||
|
||||
if (args[0] < 0)
|
||||
if (self->args[0] < 0)
|
||||
{
|
||||
FName name = ENamedName(-args[0]);
|
||||
FName name = ENamedName(-self->args[0]);
|
||||
tpl = DecalLibrary.GetDecalByName(name.GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
int decalid = args[0] + (args[1] << 8); // [KS] High byte for decals.
|
||||
int decalid = self->args[0] + (self->args[1] << 8); // [KS] High byte for decals.
|
||||
tpl = DecalLibrary.GetDecalByNum(decalid);
|
||||
}
|
||||
|
||||
|
@ -784,23 +776,22 @@ void ADecal::BeginPlay ()
|
|||
{
|
||||
if (!tpl->PicNum.Exists())
|
||||
{
|
||||
Printf("Decal actor at (%f,%f) does not have a valid texture\n", X(), Y());
|
||||
Printf("Decal actor at (%f,%f) does not have a valid texture\n", self->X(), self->Y());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Look for a wall within 64 units behind the actor. If none can be
|
||||
// found, then no decal is created, and this actor is destroyed
|
||||
// without effectively doing anything.
|
||||
if (NULL == ShootDecal(tpl, this, Sector, X(), Y(), Z(), Angles.Yaw + 180, 64., true))
|
||||
if (NULL == ShootDecal(tpl, self, self->Sector, self->X(), self->Y(), self->Z(), self->Angles.Yaw + 180, 64., true))
|
||||
{
|
||||
DPrintf (DMSG_WARNING, "Could not find a wall to stick decal to at (%f,%f)\n", X(), Y());
|
||||
DPrintf (DMSG_WARNING, "Could not find a wall to stick decal to at (%f,%f)\n", self->X(), self->Y());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DPrintf (DMSG_ERROR, "Decal actor at (%f,%f) does not have a good template\n", X(), Y());
|
||||
DPrintf (DMSG_ERROR, "Decal actor at (%f,%f) does not have a good template\n", self->X(), self->Y());
|
||||
}
|
||||
// This actor doesn't need to stick around anymore.
|
||||
Destroy();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -5772,10 +5772,7 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
|
|||
}
|
||||
|
||||
DObject::StaticPointerSubstitution (oldactor, p->mo);
|
||||
// PointerSubstitution() will also affect the bodyque, so undo that now.
|
||||
for (int ii=0; ii < BODYQUESIZE; ++ii)
|
||||
if (bodyque[ii] == p->mo)
|
||||
bodyque[ii] = oldactor;
|
||||
|
||||
E_PlayerRespawned(int(p - players));
|
||||
FBehavior::StaticStartTypedScripts (SCRIPT_Respawn, p->mo, true);
|
||||
}
|
||||
|
|
|
@ -892,7 +892,7 @@ static void P_CheckWeaponButtons (player_t *player)
|
|||
if ((player->WeaponState & ButtonChecks[i].StateFlag) &&
|
||||
(player->cmd.ucmd.buttons & ButtonChecks[i].ButtonFlag))
|
||||
{
|
||||
FState *state = weapon->GetStateForButtonName(ButtonChecks[i].StateName);
|
||||
FState *state = weapon->FindState(ButtonChecks[i].StateName);
|
||||
// [XA] don't change state if still null, so if the modder
|
||||
// sets WRF_xxx to true but forgets to define the corresponding
|
||||
// state, the weapon won't disappear. ;)
|
||||
|
|
|
@ -971,7 +971,9 @@ void G_SerializeLevel(FSerializer &arc, bool hubload)
|
|||
("level.skytexture2", level.skytexture2)
|
||||
("level.fogdensity", level.fogdensity)
|
||||
("level.outsidefogdensity", level.outsidefogdensity)
|
||||
("level.skyfog", level.skyfog);
|
||||
("level.skyfog", level.skyfog)
|
||||
("level.bodyqueslot", level.bodyqueslot)
|
||||
.Array("level.bodyque", level.bodyque, level.BODYQUESIZE);
|
||||
|
||||
// Hub transitions must keep the current total time
|
||||
if (!hubload)
|
||||
|
|
|
@ -4048,12 +4048,12 @@ void P_SetupLevel(const char *lumpname, int position, bool newGame)
|
|||
FixHoles();
|
||||
}
|
||||
|
||||
bodyqueslot = 0;
|
||||
level.bodyqueslot = 0;
|
||||
// phares 8/10/98: Clear body queue so the corpses from previous games are
|
||||
// not assumed to be from this one.
|
||||
|
||||
for (i = 0; i < BODYQUESIZE; i++)
|
||||
bodyque[i] = NULL;
|
||||
for(auto & p : level.bodyque)
|
||||
p = nullptr;
|
||||
|
||||
CreateSections(level.sections);
|
||||
|
||||
|
|
|
@ -396,38 +396,6 @@ player_t &player_t::operator=(const player_t &p)
|
|||
return *this;
|
||||
}
|
||||
|
||||
// This function supplements the pointer cleanup in dobject.cpp, because
|
||||
// player_t is not derived from DObject. (I tried it, and DestroyScan was
|
||||
// unable to properly determine the player object's type--possibly
|
||||
// because it gets staticly allocated in an array.)
|
||||
//
|
||||
// This function checks all the DObject pointers in a player_t and NULLs any
|
||||
// that match the pointer passed in. If you add any pointers that point to
|
||||
// DObject (or a subclass), add them here too.
|
||||
|
||||
size_t player_t::FixPointers (const DObject *old, DObject *rep)
|
||||
{
|
||||
APlayerPawn *replacement = static_cast<APlayerPawn *>(rep);
|
||||
size_t changed = 0;
|
||||
|
||||
// The construct *& is used in several of these to avoid the read barriers
|
||||
// that would turn the pointer we want to check to NULL if the old object
|
||||
// is pending deletion.
|
||||
if (mo == old) mo = replacement, changed++;
|
||||
if (*&poisoner == old) poisoner = replacement, changed++;
|
||||
if (*&attacker == old) attacker = replacement, changed++;
|
||||
if (*&camera == old) camera = replacement, changed++;
|
||||
if (*&Bot == old) Bot = static_cast<DBot *>(rep), changed++;
|
||||
if (ReadyWeapon == old) ReadyWeapon = static_cast<AWeapon *>(rep), changed++;
|
||||
if (PendingWeapon == old) PendingWeapon = static_cast<AWeapon *>(rep), changed++;
|
||||
if (*&PremorphWeapon == old) PremorphWeapon = static_cast<AWeapon *>(rep), changed++;
|
||||
if (psprites == old) psprites = static_cast<DPSprite *>(rep), changed++;
|
||||
if (*&ConversationNPC == old) ConversationNPC = replacement, changed++;
|
||||
if (*&ConversationPC == old) ConversationPC = replacement, changed++;
|
||||
if (*&MUSINFOactor == old) MUSINFOactor = replacement, changed++;
|
||||
return changed;
|
||||
}
|
||||
|
||||
size_t player_t::PropagateMark()
|
||||
{
|
||||
GC::Mark(mo);
|
||||
|
|
|
@ -67,7 +67,6 @@ public:
|
|||
void Interpolate(double smoothratio);
|
||||
|
||||
virtual void Serialize(FSerializer &arc);
|
||||
size_t PointerSubstitution (DObject *old, DObject *notOld);
|
||||
size_t PropagateMark();
|
||||
};
|
||||
|
||||
|
@ -535,25 +534,6 @@ void DSectorPlaneInterpolation::Serialize(FSerializer &arc)
|
|||
("attached", attached);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t DSectorPlaneInterpolation::PointerSubstitution (DObject *old, DObject *notOld)
|
||||
{
|
||||
int subst = 0;
|
||||
for(unsigned i=0; i<attached.Size(); i++)
|
||||
{
|
||||
if (attached[i] == old)
|
||||
{
|
||||
attached[i] = (DInterpolation*)notOld;
|
||||
subst++;
|
||||
}
|
||||
}
|
||||
return subst;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "vm.h"
|
||||
#include "v_text.h"
|
||||
#include "m_crc32.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
#include "gi.h"
|
||||
|
||||
|
@ -926,7 +927,7 @@ void R_InitTranslationTables ()
|
|||
|
||||
// Each player corpse has its own translation so they won't change
|
||||
// color if the player who created them changes theirs.
|
||||
for (i = 0; i < BODYQUESIZE; ++i)
|
||||
for (i = 0; i < level.BODYQUESIZE; ++i)
|
||||
{
|
||||
PushIdentityTable(TRANSLATION_PlayerCorpses);
|
||||
}
|
||||
|
|
|
@ -1048,7 +1048,7 @@ void FunctionCallEmitter::AddParameterStringConst(const FString &konst)
|
|||
|
||||
ExpEmit FunctionCallEmitter::EmitCall(VMFunctionBuilder *build, TArray<ExpEmit> *ReturnRegs)
|
||||
{
|
||||
int paramcount = 0;
|
||||
unsigned paramcount = 0;
|
||||
for (auto &func : emitters)
|
||||
{
|
||||
paramcount += func(build);
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
class Decal : Actor native
|
||||
class Decal : Actor
|
||||
{
|
||||
native void SpawnDecal();
|
||||
|
||||
override void BeginPlay()
|
||||
{
|
||||
Super.BeginPlay();
|
||||
SpawnDecal();
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue