2017-04-17 11:33:19 +00:00
/*
* *
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 2017 ZZYZX
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
2017-01-22 00:33:53 +00:00
# include "events.h"
2017-04-12 23:12:04 +00:00
# include "vm.h"
2018-11-29 17:46:28 +00:00
# include "vmintern.h"
2017-01-22 00:33:53 +00:00
# include "r_utility.h"
2017-01-22 07:36:39 +00:00
# include "g_levellocals.h"
2017-01-22 07:58:48 +00:00
# include "gi.h"
2017-01-30 07:10:33 +00:00
# include "actor.h"
2017-02-06 13:52:20 +00:00
# include "c_dispatch.h"
2017-02-06 14:02:44 +00:00
# include "d_net.h"
2019-01-30 23:28:43 +00:00
# include "g_game.h"
2018-08-15 15:46:03 +00:00
# include "info.h"
2019-02-16 20:29:46 +00:00
# include "utf8.h"
2017-01-22 00:33:53 +00:00
2019-02-02 15:43:11 +00:00
EventManager staticEventManager ;
2017-01-22 00:33:53 +00:00
2019-03-26 16:02:40 +00:00
void EventManager : : CallOnRegister ( )
{
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
{
handler - > OnRegister ( ) ;
}
}
2019-02-02 09:46:34 +00:00
bool EventManager : : RegisterHandler ( DStaticEventHandler * handler )
2017-01-22 00:33:53 +00:00
{
if ( handler = = nullptr | | handler - > ObjectFlags & OF_EuthanizeMe )
2017-01-22 06:56:57 +00:00
return false ;
2019-02-02 09:46:34 +00:00
if ( CheckHandler ( handler ) )
2017-01-22 06:56:57 +00:00
return false ;
2017-02-03 10:28:40 +00:00
handler - > OnRegister ( ) ;
2019-02-02 15:43:11 +00:00
handler - > owner = this ;
2017-02-02 17:57:00 +00:00
2017-01-22 00:33:53 +00:00
// link into normal list
2017-02-02 17:57:00 +00:00
// update: link at specific position based on order.
DStaticEventHandler * before = nullptr ;
2019-02-02 09:46:34 +00:00
for ( DStaticEventHandler * existinghandler = FirstEventHandler ; existinghandler ; existinghandler = existinghandler - > next )
2017-02-02 17:57:00 +00:00
{
2017-02-03 10:28:40 +00:00
if ( existinghandler - > Order > handler - > Order )
2017-02-02 17:57:00 +00:00
{
before = existinghandler ;
break ;
}
}
2017-02-09 13:35:45 +00:00
// (Yes, all those write barriers here are really needed!)
2017-02-02 17:57:00 +00:00
if ( before ! = nullptr )
{
// if before is not null, link it before the existing handler.
// note that before can be first handler, check for this.
2019-01-15 14:22:25 +00:00
if ( before - > prev ! = nullptr )
{
before - > prev - > next = handler ;
GC : : WriteBarrier ( before - > prev , handler ) ;
}
2017-02-02 17:57:00 +00:00
handler - > next = before ;
2017-02-09 13:35:45 +00:00
GC : : WriteBarrier ( handler , before ) ;
2017-02-02 17:57:00 +00:00
handler - > prev = before - > prev ;
2017-02-09 13:35:45 +00:00
GC : : WriteBarrier ( handler , before - > prev ) ;
2017-02-02 17:57:00 +00:00
before - > prev = handler ;
2017-02-09 13:35:45 +00:00
GC : : WriteBarrier ( before , handler ) ;
2019-02-02 09:46:34 +00:00
if ( before = = FirstEventHandler )
2017-02-09 13:35:45 +00:00
{
2019-02-02 09:46:34 +00:00
FirstEventHandler = handler ;
2017-02-09 13:35:45 +00:00
GC : : WriteBarrier ( handler ) ;
}
2017-02-02 17:57:00 +00:00
}
else
{
// so if before is null, it means add last.
// it can also mean that we have no handlers at all yet.
2019-02-02 09:46:34 +00:00
handler - > prev = LastEventHandler ;
GC : : WriteBarrier ( handler , LastEventHandler ) ;
2017-02-02 17:57:00 +00:00
handler - > next = nullptr ;
2019-02-02 09:46:34 +00:00
if ( FirstEventHandler = = nullptr ) FirstEventHandler = handler ;
LastEventHandler = handler ;
2017-02-09 13:35:45 +00:00
GC : : WriteBarrier ( handler ) ;
2017-02-02 17:57:00 +00:00
if ( handler - > prev ! = nullptr )
2017-02-09 13:35:45 +00:00
{
2017-02-02 17:57:00 +00:00
handler - > prev - > next = handler ;
2017-02-09 13:35:45 +00:00
GC : : WriteBarrier ( handler - > prev , handler ) ;
}
2017-02-02 17:57:00 +00:00
}
2017-01-30 05:50:09 +00:00
if ( handler - > IsStatic ( ) )
{
2017-01-31 04:24:39 +00:00
handler - > ObjectFlags | = OF_Transient ;
2017-01-30 05:50:09 +00:00
}
2017-02-02 17:57:00 +00:00
2017-01-22 06:56:57 +00:00
return true ;
2017-01-22 00:33:53 +00:00
}
2019-02-02 09:46:34 +00:00
bool EventManager : : UnregisterHandler ( DStaticEventHandler * handler )
2017-01-22 00:33:53 +00:00
{
if ( handler = = nullptr | | handler - > ObjectFlags & OF_EuthanizeMe )
2017-01-22 06:56:57 +00:00
return false ;
2019-02-02 09:46:34 +00:00
if ( ! CheckHandler ( handler ) )
2017-01-22 06:56:57 +00:00
return false ;
2017-02-03 10:28:40 +00:00
handler - > OnUnregister ( ) ;
2017-01-22 00:33:53 +00:00
// link out of normal list
if ( handler - > prev )
2017-02-09 13:35:45 +00:00
{
2017-01-22 00:33:53 +00:00
handler - > prev - > next = handler - > next ;
2017-02-09 13:35:45 +00:00
GC : : WriteBarrier ( handler - > prev , handler - > next ) ;
}
2017-01-22 00:33:53 +00:00
if ( handler - > next )
2017-02-09 13:35:45 +00:00
{
2017-01-22 00:33:53 +00:00
handler - > next - > prev = handler - > prev ;
2017-02-09 13:35:45 +00:00
GC : : WriteBarrier ( handler - > next , handler - > prev ) ;
}
2019-02-02 09:46:34 +00:00
if ( handler = = FirstEventHandler )
2017-02-09 13:35:45 +00:00
{
2019-02-02 09:46:34 +00:00
FirstEventHandler = handler - > next ;
2017-02-09 13:35:45 +00:00
GC : : WriteBarrier ( handler - > next ) ;
}
2019-02-02 09:46:34 +00:00
if ( handler = = LastEventHandler )
2017-02-09 13:35:45 +00:00
{
2019-02-02 09:46:34 +00:00
LastEventHandler = handler - > prev ;
2017-02-09 13:35:45 +00:00
GC : : WriteBarrier ( handler - > prev ) ;
}
2017-01-30 05:50:09 +00:00
if ( handler - > IsStatic ( ) )
2017-01-22 06:58:59 +00:00
{
2017-02-09 13:35:45 +00:00
handler - > ObjectFlags & = ~ OF_Transient ;
2017-01-30 05:50:09 +00:00
handler - > Destroy ( ) ;
2017-01-22 06:58:59 +00:00
}
2017-01-22 06:56:57 +00:00
return true ;
}
2019-02-02 09:46:34 +00:00
bool EventManager : : SendNetworkEvent ( FString name , int arg1 , int arg2 , int arg3 , bool manual )
2017-03-03 22:57:41 +00:00
{
if ( gamestate ! = GS_LEVEL )
return false ;
Net_WriteByte ( DEM_NETEVENT ) ;
Net_WriteString ( name ) ;
Net_WriteByte ( 3 ) ;
Net_WriteLong ( arg1 ) ;
Net_WriteLong ( arg2 ) ;
Net_WriteLong ( arg3 ) ;
2017-03-06 09:25:30 +00:00
Net_WriteByte ( manual ) ;
2017-03-03 22:57:41 +00:00
return true ;
}
2019-02-02 09:46:34 +00:00
bool EventManager : : CheckHandler ( DStaticEventHandler * handler )
2017-01-22 06:56:57 +00:00
{
2019-02-02 09:46:34 +00:00
for ( DStaticEventHandler * lhandler = FirstEventHandler ; lhandler ; lhandler = lhandler - > next )
2017-01-22 06:56:57 +00:00
if ( handler = = lhandler ) return true ;
return false ;
2017-01-22 00:33:53 +00:00
}
2019-02-02 09:46:34 +00:00
bool EventManager : : IsStaticType ( PClass * type )
2017-01-22 07:36:39 +00:00
{
2018-03-29 13:41:29 +00:00
assert ( type ! = nullptr ) ;
assert ( type - > IsDescendantOf ( RUNTIME_CLASS ( DStaticEventHandler ) ) ) ;
return ! type - > IsDescendantOf ( RUNTIME_CLASS ( DEventHandler ) ) ;
2017-01-22 07:36:39 +00:00
}
2019-02-02 09:46:34 +00:00
static PClass * GetHandlerClass ( const FString & typeName )
2017-01-22 07:58:48 +00:00
{
2018-03-29 13:41:29 +00:00
PClass * type = PClass : : FindClass ( typeName ) ;
2017-01-22 07:58:48 +00:00
if ( type = = nullptr )
{
2018-03-29 13:41:29 +00:00
I_Error ( " Fatal: unknown event handler class %s " , typeName . GetChars ( ) ) ;
2017-01-22 07:58:48 +00:00
}
2018-03-29 13:41:29 +00:00
else if ( ! type - > IsDescendantOf ( RUNTIME_CLASS ( DStaticEventHandler ) ) )
2017-01-22 07:58:48 +00:00
{
2018-03-29 13:41:29 +00:00
I_Error ( " Fatal: event handler class %s is not derived from StaticEventHandler " , typeName . GetChars ( ) ) ;
2017-01-22 07:58:48 +00:00
}
2018-03-29 13:41:29 +00:00
return type ;
}
2019-02-02 09:46:34 +00:00
void EventManager : : InitHandler ( PClass * type )
2018-03-29 13:41:29 +00:00
{
2017-01-22 07:58:48 +00:00
// check if type already exists, don't add twice.
bool typeExists = false ;
2019-02-02 09:46:34 +00:00
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-01-22 07:58:48 +00:00
{
if ( handler - > IsA ( type ) )
{
typeExists = true ;
break ;
}
}
if ( typeExists ) return ;
DStaticEventHandler * handler = ( DStaticEventHandler * ) type - > CreateNew ( ) ;
2019-02-02 09:46:34 +00:00
RegisterHandler ( handler ) ;
2017-01-22 07:58:48 +00:00
}
2019-02-06 14:33:19 +00:00
void EventManager : : InitStaticHandlers ( FLevelLocals * l , bool map )
2017-01-22 07:36:39 +00:00
{
2018-03-29 13:41:29 +00:00
// don't initialize map handlers if restoring from savegame.
2017-01-30 05:50:09 +00:00
if ( savegamerestore )
return ;
2017-01-22 07:36:39 +00:00
2017-02-03 11:01:15 +00:00
// just make sure
2019-02-02 15:43:11 +00:00
Shutdown ( ) ;
2019-02-06 14:33:19 +00:00
Level = l ;
2017-02-03 11:01:15 +00:00
2018-03-29 13:41:29 +00:00
// initialize event handlers from gameinfo
for ( const FString & typeName : gameinfo . EventHandlers )
2017-01-22 07:36:39 +00:00
{
2019-02-02 09:46:34 +00:00
PClass * type = GetHandlerClass ( typeName ) ;
2018-03-29 13:41:29 +00:00
// don't init the really global stuff here on startup initialization.
// don't init map-local global stuff here on level setup.
2019-02-02 09:46:34 +00:00
if ( map = = IsStaticType ( type ) )
2018-03-29 13:41:29 +00:00
continue ;
2019-02-02 09:46:34 +00:00
InitHandler ( type ) ;
2017-01-22 07:36:39 +00:00
}
2018-03-29 13:41:29 +00:00
if ( ! map )
return ;
// initialize event handlers from mapinfo
2019-02-06 14:33:19 +00:00
for ( const FString & typeName : Level - > info - > EventHandlers )
2017-01-22 07:36:39 +00:00
{
2019-02-02 09:46:34 +00:00
PClass * type = GetHandlerClass ( typeName ) ;
if ( IsStaticType ( type ) )
2018-03-29 13:41:29 +00:00
I_Error ( " Fatal: invalid event handler class %s in MAPINFO! \n Map-specific event handlers cannot be static. \n " , typeName . GetChars ( ) ) ;
2019-02-02 09:46:34 +00:00
InitHandler ( type ) ;
2017-01-22 07:36:39 +00:00
}
}
2019-02-02 15:43:11 +00:00
void EventManager : : Shutdown ( )
2017-02-03 11:01:15 +00:00
{
// delete handlers.
2019-04-06 07:06:41 +00:00
TArray < DStaticEventHandler * > list ;
2019-02-02 09:46:34 +00:00
for ( DStaticEventHandler * handler = LastEventHandler ; handler ; handler = handler - > prev )
2019-04-06 07:06:41 +00:00
{
list . Push ( handler ) ;
}
for ( auto handler : list )
2017-02-03 11:01:15 +00:00
{
2019-02-02 15:43:11 +00:00
handler - > Destroy ( ) ;
2017-02-03 11:01:15 +00:00
}
2019-02-09 07:46:13 +00:00
FirstEventHandler = LastEventHandler = nullptr ;
2017-02-03 11:01:15 +00:00
}
2019-02-03 09:04:49 +00:00
# define DEFINE_EVENT_LOOPER(name, play) void EventManager::name() \
2017-01-23 22:17:12 +00:00
{ \
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( play ) ) staticEventManager . name ( ) ; \
2019-02-02 09:46:34 +00:00
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next ) \
2017-01-23 22:17:12 +00:00
handler - > name ( ) ; \
2017-01-22 00:33:53 +00:00
}
2019-02-02 15:43:11 +00:00
2017-01-30 05:50:09 +00:00
// note for the functions below.
// *Unsafe is executed on EVERY map load/close, including savegame loading, etc.
// There is no point in allowing non-static handlers to receive unsafe event separately, as there is no point in having static handlers receive safe event.
// Because the main point of safe WorldLoaded/Unloading is that it will be preserved in savegames.
2019-02-02 09:46:34 +00:00
void EventManager : : WorldLoaded ( )
2017-01-30 05:50:09 +00:00
{
2019-02-02 09:46:34 +00:00
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-01-30 05:50:09 +00:00
{
2019-03-02 12:50:58 +00:00
if ( ! handler - > IsStatic ( ) & & savegamerestore ) continue ; // don't execute WorldLoaded for handlers loaded from the savegame.
2017-01-30 05:50:09 +00:00
handler - > WorldLoaded ( ) ;
}
}
2019-02-02 09:46:34 +00:00
void EventManager : : WorldUnloaded ( )
2017-01-30 05:50:09 +00:00
{
2019-02-02 15:43:11 +00:00
for ( DStaticEventHandler * handler = LastEventHandler ; handler ; handler = handler - > prev )
2017-01-30 05:50:09 +00:00
{
2017-01-30 06:47:15 +00:00
handler - > WorldUnloaded ( ) ;
2017-01-30 05:50:09 +00:00
}
}
2019-02-02 15:43:11 +00:00
bool EventManager : : ShouldCallStatic ( bool forplay )
2017-01-30 05:50:09 +00:00
{
2019-02-02 15:43:11 +00:00
return this ! = & staticEventManager & & Level = = primaryLevel ;
2017-01-30 05:50:09 +00:00
}
2019-02-02 09:46:34 +00:00
void EventManager : : WorldThingSpawned ( AActor * actor )
2017-01-30 06:47:15 +00:00
{
2017-01-30 07:10:33 +00:00
// don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever.
if ( actor - > ObjectFlags & OF_EuthanizeMe )
return ;
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . WorldThingSpawned ( actor ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-01-30 06:47:15 +00:00
handler - > WorldThingSpawned ( actor ) ;
}
2019-02-02 09:46:34 +00:00
void EventManager : : WorldThingDied ( AActor * actor , AActor * inflictor )
2017-01-31 00:07:00 +00:00
{
// don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever.
if ( actor - > ObjectFlags & OF_EuthanizeMe )
return ;
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . WorldThingDied ( actor , inflictor ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-01-31 00:07:00 +00:00
handler - > WorldThingDied ( actor , inflictor ) ;
}
2019-02-02 09:46:34 +00:00
void EventManager : : WorldThingRevived ( AActor * actor )
2017-01-31 02:11:09 +00:00
{
// don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever.
if ( actor - > ObjectFlags & OF_EuthanizeMe )
return ;
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . WorldThingRevived ( actor ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-01-31 02:11:09 +00:00
handler - > WorldThingRevived ( actor ) ;
}
2019-02-02 09:46:34 +00:00
void EventManager : : WorldThingDamaged ( AActor * actor , AActor * inflictor , AActor * source , int damage , FName mod , int flags , DAngle angle )
2017-01-31 02:35:44 +00:00
{
// don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever.
if ( actor - > ObjectFlags & OF_EuthanizeMe )
return ;
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . WorldThingDamaged ( actor , inflictor , source , damage , mod , flags , angle ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-01-31 02:35:44 +00:00
handler - > WorldThingDamaged ( actor , inflictor , source , damage , mod , flags , angle ) ;
}
2019-02-02 09:46:34 +00:00
void EventManager : : WorldThingDestroyed ( AActor * actor )
2017-01-31 00:07:00 +00:00
{
// don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever.
if ( actor - > ObjectFlags & OF_EuthanizeMe )
return ;
2017-01-31 02:53:18 +00:00
// don't call anything for non-spawned things (i.e. those that were created, but immediately destroyed)
// this is because Destroyed should be reverse of Spawned. we don't want to catch random inventory give failures.
if ( ! ( actor - > ObjectFlags & OF_Spawned ) )
return ;
2019-02-02 15:43:11 +00:00
for ( DStaticEventHandler * handler = LastEventHandler ; handler ; handler = handler - > prev )
2017-01-31 00:07:00 +00:00
handler - > WorldThingDestroyed ( actor ) ;
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . WorldThingDestroyed ( actor ) ;
2017-01-31 00:07:00 +00:00
}
2019-02-02 09:46:34 +00:00
void EventManager : : WorldLinePreActivated ( line_t * line , AActor * actor , int activationType , bool * shouldactivate )
2018-03-24 14:59:20 +00:00
{
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . WorldLinePreActivated ( line , actor , activationType , shouldactivate ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2018-04-14 08:33:17 +00:00
handler - > WorldLinePreActivated ( line , actor , activationType , shouldactivate ) ;
2018-03-24 14:59:20 +00:00
}
2019-02-02 09:46:34 +00:00
void EventManager : : WorldLineActivated ( line_t * line , AActor * actor , int activationType )
2018-03-17 15:01:47 +00:00
{
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . WorldLineActivated ( line , actor , activationType ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2018-04-14 08:33:17 +00:00
handler - > WorldLineActivated ( line , actor , activationType ) ;
2018-03-17 15:01:47 +00:00
}
2019-02-02 09:46:34 +00:00
int EventManager : : WorldSectorDamaged ( sector_t * sector , AActor * source , int damage , FName damagetype , int part , DVector3 position , bool isradius )
2018-11-06 02:59:17 +00:00
{
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . WorldSectorDamaged ( sector , source , damage , damagetype , part , position , isradius ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2018-11-06 02:59:17 +00:00
damage = handler - > WorldSectorDamaged ( sector , source , damage , damagetype , part , position , isradius ) ;
return damage ;
}
2019-02-02 09:46:34 +00:00
int EventManager : : WorldLineDamaged ( line_t * line , AActor * source , int damage , FName damagetype , int side , DVector3 position , bool isradius )
2018-11-06 02:59:17 +00:00
{
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . WorldLineDamaged ( line , source , damage , damagetype , side , position , isradius ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2018-11-06 02:59:17 +00:00
damage = handler - > WorldLineDamaged ( line , source , damage , damagetype , side , position , isradius ) ;
return damage ;
}
2019-02-02 09:46:34 +00:00
void EventManager : : PlayerEntered ( int num , bool fromhub )
2017-02-02 18:26:56 +00:00
{
2017-02-02 18:46:10 +00:00
// this event can happen during savegamerestore. make sure that local handlers don't receive it.
// actually, global handlers don't want it too.
if ( savegamerestore & & ! fromhub )
return ;
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . PlayerEntered ( num , fromhub ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-02-02 18:46:10 +00:00
handler - > PlayerEntered ( num , fromhub ) ;
2017-02-02 18:26:56 +00:00
}
2019-02-02 09:46:34 +00:00
void EventManager : : PlayerRespawned ( int num )
2017-02-02 18:26:56 +00:00
{
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . PlayerRespawned ( num ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-02-02 18:26:56 +00:00
handler - > PlayerRespawned ( num ) ;
}
2019-02-02 09:46:34 +00:00
void EventManager : : PlayerDied ( int num )
2017-02-02 18:26:56 +00:00
{
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . PlayerDied ( num ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-02-02 18:26:56 +00:00
handler - > PlayerDied ( num ) ;
}
2019-02-02 09:46:34 +00:00
void EventManager : : PlayerDisconnected ( int num )
2017-02-02 18:26:56 +00:00
{
2019-02-02 15:43:11 +00:00
for ( DStaticEventHandler * handler = LastEventHandler ; handler ; handler = handler - > prev )
2017-02-02 18:26:56 +00:00
handler - > PlayerDisconnected ( num ) ;
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( true ) ) staticEventManager . PlayerDisconnected ( num ) ;
2017-02-02 18:26:56 +00:00
}
2019-02-02 09:46:34 +00:00
bool EventManager : : Responder ( const event_t * ev )
2017-02-03 10:28:40 +00:00
{
2017-03-07 09:43:14 +00:00
bool uiProcessorsFound = false ;
2017-02-03 10:28:40 +00:00
if ( ev - > type = = EV_GUI_Event )
{
// iterate handlers back to front by order, and give them this event.
2019-02-02 15:43:11 +00:00
for ( DStaticEventHandler * handler = LastEventHandler ; handler ; handler = handler - > prev )
2017-02-03 10:28:40 +00:00
{
2017-03-07 09:43:14 +00:00
if ( handler - > IsUiProcessor )
{
uiProcessorsFound = true ;
if ( handler - > UiProcess ( ev ) )
return true ; // event was processed
}
2017-02-03 10:28:40 +00:00
}
}
else
{
// not sure if we want to handle device changes, but whatevs.
2019-02-02 15:43:11 +00:00
for ( DStaticEventHandler * handler = LastEventHandler ; handler ; handler = handler - > prev )
2017-02-03 10:28:40 +00:00
{
2017-03-07 09:43:14 +00:00
if ( handler - > IsUiProcessor )
uiProcessorsFound = true ;
2017-02-03 18:34:34 +00:00
if ( handler - > InputProcess ( ev ) )
2017-02-03 10:28:40 +00:00
return true ; // event was processed
}
}
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( false ) ) uiProcessorsFound = staticEventManager . Responder ( ev ) ;
2017-02-03 10:28:40 +00:00
2017-03-07 09:43:14 +00:00
return ( uiProcessorsFound & & ( ev - > type = = EV_Mouse ) ) ; // mouse events are eaten by the event system if there are any uiprocessors.
2017-02-03 10:28:40 +00:00
}
2019-02-02 09:46:34 +00:00
void EventManager : : Console ( int player , FString name , int arg1 , int arg2 , int arg3 , bool manual )
2017-02-06 13:52:20 +00:00
{
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( false ) ) staticEventManager . Console ( player , name , arg1 , arg2 , arg3 , manual ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-03-06 09:25:30 +00:00
handler - > ConsoleProcess ( player , name , arg1 , arg2 , arg3 , manual ) ;
2017-02-06 13:52:20 +00:00
}
2019-02-02 09:46:34 +00:00
void EventManager : : RenderOverlay ( EHudState state )
2017-03-29 21:51:53 +00:00
{
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( false ) ) staticEventManager . RenderOverlay ( state ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-03-29 21:51:53 +00:00
handler - > RenderOverlay ( state ) ;
}
2019-06-23 01:02:35 +00:00
void EventManager : : RenderUnderlay ( EHudState state )
{
if ( ShouldCallStatic ( false ) ) staticEventManager . RenderUnderlay ( state ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
handler - > RenderUnderlay ( state ) ;
}
2019-02-02 09:46:34 +00:00
bool EventManager : : CheckUiProcessors ( )
2017-02-03 10:28:40 +00:00
{
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( false ) )
{
if ( staticEventManager . CheckUiProcessors ( ) ) return true ;
}
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-02-03 10:28:40 +00:00
if ( handler - > IsUiProcessor )
return true ;
2017-02-03 18:34:34 +00:00
return false ;
}
2019-02-02 09:46:34 +00:00
bool EventManager : : CheckRequireMouse ( )
2017-02-03 18:34:34 +00:00
{
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( false ) )
{
if ( staticEventManager . CheckRequireMouse ( ) ) return true ;
}
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2017-02-03 18:34:34 +00:00
if ( handler - > IsUiProcessor & & handler - > RequireMouse )
return true ;
2017-02-03 10:28:40 +00:00
return false ;
}
2019-02-02 09:46:34 +00:00
bool EventManager : : CheckReplacement ( PClassActor * replacee , PClassActor * * replacement )
2018-08-15 15:46:03 +00:00
{
2018-08-16 18:46:40 +00:00
bool final = false ;
2019-02-02 15:43:11 +00:00
// This is play scope but unlike in-game events needs to be handled like UI by static handlers.
if ( ShouldCallStatic ( false ) ) final = staticEventManager . CheckReplacement ( replacee , replacement ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2018-08-16 18:46:40 +00:00
handler - > CheckReplacement ( replacee , replacement , & final ) ;
return final ;
2018-08-15 15:46:03 +00:00
}
2019-02-02 09:46:34 +00:00
bool EventManager : : CheckReplacee ( PClassActor * * replacee , PClassActor * replacement )
2019-01-28 00:03:04 +00:00
{
bool final = false ;
2019-02-02 15:43:11 +00:00
if ( ShouldCallStatic ( false ) ) final = staticEventManager . CheckReplacee ( replacee , replacement ) ;
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2019-01-28 00:03:04 +00:00
handler - > CheckReplacee ( replacee , replacement , & final ) ;
return final ;
}
2019-02-02 15:43:11 +00:00
void EventManager : : NewGame ( )
2018-09-15 02:57:07 +00:00
{
2019-02-02 15:43:11 +00:00
//This is called separately for static and local handlers, so no forwarding here.
for ( DStaticEventHandler * handler = FirstEventHandler ; handler ; handler = handler - > next )
2018-09-15 02:57:07 +00:00
{
2019-02-02 15:43:11 +00:00
handler - > NewGame ( ) ;
2018-09-15 02:57:07 +00:00
}
}
2017-01-30 06:47:15 +00:00
// normal event loopers (non-special, argument-less)
2019-02-02 15:43:11 +00:00
DEFINE_EVENT_LOOPER ( RenderFrame , false )
DEFINE_EVENT_LOOPER ( WorldLightning , true )
DEFINE_EVENT_LOOPER ( WorldTick , true )
DEFINE_EVENT_LOOPER ( UiTick , false )
DEFINE_EVENT_LOOPER ( PostUiTick , false )
2017-01-22 00:33:53 +00:00
// declarations
2017-02-26 09:58:22 +00:00
IMPLEMENT_CLASS ( DStaticEventHandler , false , true ) ;
IMPLEMENT_POINTERS_START ( DStaticEventHandler )
IMPLEMENT_POINTER ( next )
IMPLEMENT_POINTER ( prev )
IMPLEMENT_POINTERS_END
2017-01-22 00:33:53 +00:00
IMPLEMENT_CLASS ( DEventHandler , false , false ) ;
2017-02-03 10:28:40 +00:00
DEFINE_FIELD_X ( StaticEventHandler , DStaticEventHandler , Order ) ;
DEFINE_FIELD_X ( StaticEventHandler , DStaticEventHandler , IsUiProcessor ) ;
2017-02-03 18:34:34 +00:00
DEFINE_FIELD_X ( StaticEventHandler , DStaticEventHandler , RequireMouse ) ;
2017-01-22 00:33:53 +00:00
2017-03-06 21:27:51 +00:00
DEFINE_FIELD_X ( RenderEvent , FRenderEvent , ViewPos ) ;
DEFINE_FIELD_X ( RenderEvent , FRenderEvent , ViewAngle ) ;
DEFINE_FIELD_X ( RenderEvent , FRenderEvent , ViewPitch ) ;
DEFINE_FIELD_X ( RenderEvent , FRenderEvent , ViewRoll ) ;
DEFINE_FIELD_X ( RenderEvent , FRenderEvent , FracTic ) ;
DEFINE_FIELD_X ( RenderEvent , FRenderEvent , Camera ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , IsSaveGame ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , IsReopen ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , Thing ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , Inflictor ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , Damage ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , DamageSource ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , DamageType ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , DamageFlags ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , DamageAngle ) ;
2018-03-17 15:01:47 +00:00
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , ActivatedLine ) ;
2018-04-14 08:33:17 +00:00
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , ActivationType ) ;
2018-03-24 14:59:20 +00:00
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , ShouldActivate ) ;
2018-11-06 02:59:17 +00:00
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , DamageSectorPart ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , DamageLine ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , DamageSector ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , DamageLineSide ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , DamagePosition ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , DamageIsRadius ) ;
DEFINE_FIELD_X ( WorldEvent , FWorldEvent , NewDamage ) ;
2017-03-06 21:27:51 +00:00
DEFINE_FIELD_X ( PlayerEvent , FPlayerEvent , PlayerNumber ) ;
DEFINE_FIELD_X ( PlayerEvent , FPlayerEvent , IsReturn ) ;
DEFINE_FIELD_X ( UiEvent , FUiEvent , Type ) ;
DEFINE_FIELD_X ( UiEvent , FUiEvent , KeyString ) ;
DEFINE_FIELD_X ( UiEvent , FUiEvent , KeyChar ) ;
DEFINE_FIELD_X ( UiEvent , FUiEvent , MouseX ) ;
DEFINE_FIELD_X ( UiEvent , FUiEvent , MouseY ) ;
DEFINE_FIELD_X ( UiEvent , FUiEvent , IsShift ) ;
DEFINE_FIELD_X ( UiEvent , FUiEvent , IsAlt ) ;
DEFINE_FIELD_X ( UiEvent , FUiEvent , IsCtrl ) ;
DEFINE_FIELD_X ( InputEvent , FInputEvent , Type ) ;
DEFINE_FIELD_X ( InputEvent , FInputEvent , KeyScan ) ;
DEFINE_FIELD_X ( InputEvent , FInputEvent , KeyString ) ;
DEFINE_FIELD_X ( InputEvent , FInputEvent , KeyChar ) ;
DEFINE_FIELD_X ( InputEvent , FInputEvent , MouseX ) ;
DEFINE_FIELD_X ( InputEvent , FInputEvent , MouseY ) ;
DEFINE_FIELD_X ( ConsoleEvent , FConsoleEvent , Player )
DEFINE_FIELD_X ( ConsoleEvent , FConsoleEvent , Name )
DEFINE_FIELD_X ( ConsoleEvent , FConsoleEvent , Args )
DEFINE_FIELD_X ( ConsoleEvent , FConsoleEvent , IsManual )
2017-02-06 13:52:20 +00:00
2018-08-15 15:46:03 +00:00
DEFINE_FIELD_X ( ReplaceEvent , FReplaceEvent , Replacee )
DEFINE_FIELD_X ( ReplaceEvent , FReplaceEvent , Replacement )
2018-08-16 18:46:40 +00:00
DEFINE_FIELD_X ( ReplaceEvent , FReplaceEvent , IsFinal )
2018-08-15 15:46:03 +00:00
2019-01-28 00:03:04 +00:00
DEFINE_FIELD_X ( ReplacedEvent , FReplacedEvent , Replacee )
DEFINE_FIELD_X ( ReplacedEvent , FReplacedEvent , Replacement )
DEFINE_FIELD_X ( ReplacedEvent , FReplacedEvent , IsFinal )
2017-02-03 18:44:27 +00:00
DEFINE_ACTION_FUNCTION ( DStaticEventHandler , SetOrder )
{
PARAM_SELF_PROLOGUE ( DStaticEventHandler ) ;
PARAM_INT ( order ) ;
self - > Order = order ;
return 0 ;
}
2017-03-03 22:57:41 +00:00
DEFINE_ACTION_FUNCTION ( DEventHandler , SendNetworkEvent )
{
PARAM_PROLOGUE ;
PARAM_STRING ( name ) ;
2018-11-17 09:03:40 +00:00
PARAM_INT ( arg1 ) ;
PARAM_INT ( arg2 ) ;
PARAM_INT ( arg3 ) ;
2017-03-03 22:57:41 +00:00
//
2019-02-02 15:43:11 +00:00
ACTION_RETURN_BOOL ( currentVMLevel - > localEventManager - > SendNetworkEvent ( name , arg1 , arg2 , arg3 , false ) ) ;
2017-03-03 22:57:41 +00:00
}
2017-01-30 07:33:06 +00:00
DEFINE_ACTION_FUNCTION ( DEventHandler , Find )
{
PARAM_PROLOGUE ;
PARAM_CLASS ( t , DStaticEventHandler ) ;
2019-02-02 15:43:11 +00:00
for ( DStaticEventHandler * handler = currentVMLevel - > localEventManager - > FirstEventHandler ; handler ; handler = handler - > next )
2017-01-30 07:33:06 +00:00
if ( handler - > GetClass ( ) = = t ) // check precise class
ACTION_RETURN_OBJECT ( handler ) ;
ACTION_RETURN_OBJECT ( nullptr ) ;
}
// we might later want to change this
DEFINE_ACTION_FUNCTION ( DStaticEventHandler , Find )
2017-01-22 00:56:15 +00:00
{
PARAM_PROLOGUE ;
2017-01-22 06:56:57 +00:00
PARAM_CLASS ( t , DStaticEventHandler ) ;
2019-02-02 15:43:11 +00:00
for ( DStaticEventHandler * handler = staticEventManager . FirstEventHandler ; handler ; handler = handler - > next )
2017-01-22 00:56:15 +00:00
if ( handler - > GetClass ( ) = = t ) // check precise class
ACTION_RETURN_OBJECT ( handler ) ;
ACTION_RETURN_OBJECT ( nullptr ) ;
}
2017-02-02 17:57:00 +00:00
2018-11-29 17:46:28 +00:00
// Check if the handler implements a callback for this event. This is used to avoid setting up the data for any event that the handler won't process
static bool isEmpty ( VMFunction * func )
{
auto code = static_cast < VMScriptFunction * > ( func ) - > Code ;
return ( code = = nullptr | | code - > word = = ( 0x00808000 | OP_RET ) ) ;
}
2018-08-15 15:46:03 +00:00
2018-11-29 17:46:28 +00:00
static bool isEmptyInt ( VMFunction * func )
{
auto code = static_cast < VMScriptFunction * > ( func ) - > Code ;
return ( code = = nullptr | | code - > word = = ( 0x00048000 | OP_RET ) ) ;
}
2018-09-15 02:57:07 +00:00
2017-01-31 00:07:00 +00:00
// ===========================================
//
// Event handlers
//
// ===========================================
2017-02-03 10:28:40 +00:00
void DStaticEventHandler : : OnRegister ( )
{
IFVIRTUAL ( DStaticEventHandler , OnRegister )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2017-02-03 10:28:40 +00:00
VMValue params [ 1 ] = { ( DStaticEventHandler * ) this } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 1 , nullptr , 0 ) ;
2017-02-03 10:28:40 +00:00
}
}
void DStaticEventHandler : : OnUnregister ( )
{
IFVIRTUAL ( DStaticEventHandler , OnUnregister )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2017-02-03 10:28:40 +00:00
VMValue params [ 1 ] = { ( DStaticEventHandler * ) this } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 1 , nullptr , 0 ) ;
2017-02-03 10:28:40 +00:00
}
}
2019-02-02 09:46:34 +00:00
FWorldEvent EventManager : : SetupWorldEvent ( )
2017-01-30 23:28:47 +00:00
{
2017-03-06 21:27:51 +00:00
FWorldEvent e ;
e . IsSaveGame = savegamerestore ;
2019-02-02 15:43:11 +00:00
e . IsReopen = Level - > FromSnapshot & & ! savegamerestore ; // each one by itself isnt helpful, but with hub load we have savegamerestore==0 and level.FromSnapshot==1.
2017-03-06 21:27:51 +00:00
e . DamageAngle = 0.0 ;
2017-01-30 23:28:47 +00:00
return e ;
}
void DStaticEventHandler : : WorldLoaded ( )
{
IFVIRTUAL ( DStaticEventHandler , WorldLoaded )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2017-03-06 21:27:51 +00:00
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-01-30 23:28:47 +00:00
}
}
void DStaticEventHandler : : WorldUnloaded ( )
{
IFVIRTUAL ( DStaticEventHandler , WorldUnloaded )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2017-03-06 21:27:51 +00:00
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-01-30 23:28:47 +00:00
}
}
void DStaticEventHandler : : WorldThingSpawned ( AActor * actor )
{
IFVIRTUAL ( DStaticEventHandler , WorldThingSpawned )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2017-03-06 21:27:51 +00:00
e . Thing = actor ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-01-30 23:28:47 +00:00
}
}
2017-01-31 00:07:00 +00:00
void DStaticEventHandler : : WorldThingDied ( AActor * actor , AActor * inflictor )
{
IFVIRTUAL ( DStaticEventHandler , WorldThingDied )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2017-03-06 21:27:51 +00:00
e . Thing = actor ;
e . Inflictor = inflictor ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-01-31 02:11:09 +00:00
}
}
void DStaticEventHandler : : WorldThingRevived ( AActor * actor )
{
IFVIRTUAL ( DStaticEventHandler , WorldThingRevived )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2017-03-06 21:27:51 +00:00
e . Thing = actor ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-01-31 02:35:44 +00:00
}
}
void DStaticEventHandler : : WorldThingDamaged ( AActor * actor , AActor * inflictor , AActor * source , int damage , FName mod , int flags , DAngle angle )
{
IFVIRTUAL ( DStaticEventHandler , WorldThingDamaged )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2017-03-06 21:27:51 +00:00
e . Thing = actor ;
2017-08-09 13:16:33 +00:00
e . Inflictor = inflictor ;
2017-03-06 21:27:51 +00:00
e . Damage = damage ;
e . DamageSource = source ;
e . DamageType = mod ;
e . DamageFlags = flags ;
e . DamageAngle = angle ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-01-31 00:07:00 +00:00
}
}
void DStaticEventHandler : : WorldThingDestroyed ( AActor * actor )
{
IFVIRTUAL ( DStaticEventHandler , WorldThingDestroyed )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2017-03-06 21:27:51 +00:00
e . Thing = actor ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-01-31 00:07:00 +00:00
}
}
2018-04-14 08:33:17 +00:00
void DStaticEventHandler : : WorldLinePreActivated ( line_t * line , AActor * actor , int activationType , bool * shouldactivate )
2018-03-24 14:59:20 +00:00
{
IFVIRTUAL ( DStaticEventHandler , WorldLinePreActivated )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2018-03-24 14:59:20 +00:00
e . Thing = actor ;
e . ActivatedLine = line ;
2018-04-14 08:33:17 +00:00
e . ActivationType = activationType ;
2018-03-24 14:59:20 +00:00
e . ShouldActivate = * shouldactivate ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
VMCall ( func , params , 2 , nullptr , 0 ) ;
* shouldactivate = e . ShouldActivate ;
}
}
2018-04-14 08:33:17 +00:00
void DStaticEventHandler : : WorldLineActivated ( line_t * line , AActor * actor , int activationType )
2018-03-17 15:01:47 +00:00
{
IFVIRTUAL ( DStaticEventHandler , WorldLineActivated )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2018-03-17 15:01:47 +00:00
e . Thing = actor ;
e . ActivatedLine = line ;
2018-04-14 08:33:17 +00:00
e . ActivationType = activationType ;
2018-03-17 15:01:47 +00:00
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
VMCall ( func , params , 2 , nullptr , 0 ) ;
}
}
2018-11-06 02:59:17 +00:00
int DStaticEventHandler : : WorldSectorDamaged ( sector_t * sector , AActor * source , int damage , FName damagetype , int part , DVector3 position , bool isradius )
{
IFVIRTUAL ( DStaticEventHandler , WorldSectorDamaged )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return damage ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2018-11-06 02:59:17 +00:00
e . DamageSource = source ;
e . DamageSector = sector ;
e . NewDamage = e . Damage = damage ;
e . DamageType = damagetype ;
e . DamageSectorPart = part ;
e . DamagePosition = position ;
e . DamageIsRadius = isradius ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
VMCall ( func , params , 2 , nullptr , 0 ) ;
return e . NewDamage ;
}
return damage ;
}
int DStaticEventHandler : : WorldLineDamaged ( line_t * line , AActor * source , int damage , FName damagetype , int side , DVector3 position , bool isradius )
{
IFVIRTUAL ( DStaticEventHandler , WorldLineDamaged )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return damage ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2018-11-06 02:59:17 +00:00
e . DamageSource = source ;
e . DamageLine = line ;
e . NewDamage = e . Damage = damage ;
e . DamageType = damagetype ;
e . DamageLineSide = side ;
e . DamagePosition = position ;
e . DamageIsRadius = isradius ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
VMCall ( func , params , 2 , nullptr , 0 ) ;
return e . NewDamage ;
}
return damage ;
}
2017-01-31 00:07:00 +00:00
void DStaticEventHandler : : WorldLightning ( )
{
IFVIRTUAL ( DStaticEventHandler , WorldLightning )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FWorldEvent e = owner - > SetupWorldEvent ( ) ;
2017-03-06 21:27:51 +00:00
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-01-31 00:07:00 +00:00
}
}
2017-01-31 01:24:38 +00:00
void DStaticEventHandler : : WorldTick ( )
{
IFVIRTUAL ( DStaticEventHandler , WorldTick )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2017-03-09 13:08:49 +00:00
VMValue params [ 1 ] = { ( DStaticEventHandler * ) this } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 1 , nullptr , 0 ) ;
2017-01-31 01:24:38 +00:00
}
}
2019-02-02 09:46:34 +00:00
FRenderEvent EventManager : : SetupRenderEvent ( )
2017-01-30 23:28:47 +00:00
{
2017-03-06 21:27:51 +00:00
FRenderEvent e ;
2018-06-19 09:20:36 +00:00
auto & vp = r_viewpoint ;
e . ViewPos = vp . Pos ;
e . ViewAngle = vp . Angles . Yaw ;
e . ViewPitch = vp . Angles . Pitch ;
e . ViewRoll = vp . Angles . Roll ;
e . FracTic = vp . TicFrac ;
e . Camera = vp . camera ;
2017-01-30 23:28:47 +00:00
return e ;
}
void DStaticEventHandler : : RenderFrame ( )
{
2018-11-18 16:10:55 +00:00
/* This is intentionally and permanently disabled.
2017-01-30 23:28:47 +00:00
IFVIRTUAL ( DStaticEventHandler , RenderFrame )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FRenderEvent e = owner - > SetupRenderEvent ;
2017-03-06 21:27:51 +00:00
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-02-03 11:29:17 +00:00
}
2018-11-18 16:10:55 +00:00
*/
2017-02-03 11:29:17 +00:00
}
2017-03-29 21:51:53 +00:00
void DStaticEventHandler : : RenderOverlay ( EHudState state )
2017-02-03 11:29:17 +00:00
{
IFVIRTUAL ( DStaticEventHandler , RenderOverlay )
{
// don't create excessive DObjects if not going to be processed anyway
2019-06-23 01:02:35 +00:00
if ( isEmpty ( func ) ) return ;
FRenderEvent e = owner - > SetupRenderEvent ( ) ;
e . HudState = int ( state ) ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
VMCall ( func , params , 2 , nullptr , 0 ) ;
}
}
void DStaticEventHandler : : RenderUnderlay ( EHudState state )
{
IFVIRTUAL ( DStaticEventHandler , RenderUnderlay )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2019-02-02 15:43:11 +00:00
FRenderEvent e = owner - > SetupRenderEvent ( ) ;
2017-03-29 21:51:53 +00:00
e . HudState = int ( state ) ;
2017-03-06 21:27:51 +00:00
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-01-30 23:28:47 +00:00
}
}
2017-01-22 00:33:53 +00:00
2017-02-02 18:46:10 +00:00
void DStaticEventHandler : : PlayerEntered ( int num , bool fromhub )
2017-02-02 18:26:56 +00:00
{
IFVIRTUAL ( DStaticEventHandler , PlayerEntered )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2017-03-06 21:27:51 +00:00
FPlayerEvent e = { num , fromhub } ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-02-02 18:26:56 +00:00
}
}
void DStaticEventHandler : : PlayerRespawned ( int num )
{
IFVIRTUAL ( DStaticEventHandler , PlayerRespawned )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2017-03-06 21:27:51 +00:00
FPlayerEvent e = { num , false } ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-02-02 18:26:56 +00:00
}
}
void DStaticEventHandler : : PlayerDied ( int num )
{
IFVIRTUAL ( DStaticEventHandler , PlayerDied )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2017-03-06 21:27:51 +00:00
FPlayerEvent e = { num , false } ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-02-02 18:26:56 +00:00
}
}
void DStaticEventHandler : : PlayerDisconnected ( int num )
{
IFVIRTUAL ( DStaticEventHandler , PlayerDisconnected )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2017-03-06 21:27:51 +00:00
FPlayerEvent e = { num , false } ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-02-02 18:26:56 +00:00
}
}
2017-03-07 09:43:14 +00:00
FUiEvent : : FUiEvent ( const event_t * ev )
2017-01-22 00:33:53 +00:00
{
2017-03-06 21:27:51 +00:00
Type = ( EGUIEvent ) ev - > subtype ;
KeyChar = 0 ;
IsShift = false ;
IsAlt = false ;
IsCtrl = false ;
MouseX = 0 ;
MouseY = 0 ;
// we don't want the modders to remember what weird fields mean what for what events.
switch ( ev - > subtype )
{
case EV_GUI_None :
break ;
case EV_GUI_KeyDown :
case EV_GUI_KeyRepeat :
case EV_GUI_KeyUp :
KeyChar = ev - > data1 ;
KeyString = FString ( char ( ev - > data1 ) ) ;
IsShift = ! ! ( ev - > data3 & GKM_SHIFT ) ;
IsAlt = ! ! ( ev - > data3 & GKM_ALT ) ;
IsCtrl = ! ! ( ev - > data3 & GKM_CTRL ) ;
break ;
case EV_GUI_Char :
KeyChar = ev - > data1 ;
2019-02-16 20:29:46 +00:00
KeyString = MakeUTF8 ( ev - > data1 ) ;
2017-03-06 21:27:51 +00:00
IsAlt = ! ! ev - > data2 ; // only true for Win32, not sure about SDL
break ;
default : // mouse event
// note: SDL input doesn't seem to provide these at all
//Printf("Mouse data: %d, %d, %d, %d\n", ev->x, ev->y, ev->data1, ev->data2);
MouseX = ev - > data1 ;
MouseY = ev - > data2 ;
IsShift = ! ! ( ev - > data3 & GKM_SHIFT ) ;
IsAlt = ! ! ( ev - > data3 & GKM_ALT ) ;
IsCtrl = ! ! ( ev - > data3 & GKM_CTRL ) ;
break ;
}
2017-01-22 00:33:53 +00:00
}
2017-02-02 17:57:00 +00:00
2017-03-07 09:43:14 +00:00
bool DStaticEventHandler : : UiProcess ( const event_t * ev )
2017-02-03 10:28:40 +00:00
{
IFVIRTUAL ( DStaticEventHandler , UiProcess )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmptyInt ( func ) ) return false ;
2017-03-06 21:27:51 +00:00
FUiEvent e = ev ;
2017-02-03 10:28:40 +00:00
int processed ;
VMReturn results [ 1 ] = { & processed } ;
2017-03-06 21:27:51 +00:00
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , results , 1 ) ;
2017-02-03 10:28:40 +00:00
return ! ! processed ;
}
return false ;
}
2017-03-07 09:43:14 +00:00
FInputEvent : : FInputEvent ( const event_t * ev )
2017-02-02 17:57:00 +00:00
{
2017-03-06 21:27:51 +00:00
Type = ( EGenericEvent ) ev - > type ;
// we don't want the modders to remember what weird fields mean what for what events.
KeyScan = 0 ;
KeyChar = 0 ;
MouseX = 0 ;
MouseY = 0 ;
switch ( Type )
{
case EV_None :
break ;
case EV_KeyDown :
case EV_KeyUp :
KeyScan = ev - > data1 ;
KeyChar = ev - > data2 ;
KeyString = FString ( char ( ev - > data1 ) ) ;
break ;
case EV_Mouse :
MouseX = ev - > x ;
MouseY = ev - > y ;
break ;
default :
break ; // EV_DeviceChange = wat?
}
2017-02-03 10:28:40 +00:00
}
2017-02-02 17:57:00 +00:00
2017-03-07 09:43:14 +00:00
bool DStaticEventHandler : : InputProcess ( const event_t * ev )
2017-02-03 10:28:40 +00:00
{
IFVIRTUAL ( DStaticEventHandler , InputProcess )
2017-02-02 17:57:00 +00:00
{
2017-02-03 10:28:40 +00:00
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmptyInt ( func ) ) return false ;
2017-03-06 21:27:51 +00:00
FInputEvent e = ev ;
2017-02-03 10:28:40 +00:00
//
int processed ;
VMReturn results [ 1 ] = { & processed } ;
2017-03-06 21:27:51 +00:00
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , results , 1 ) ;
2017-02-03 10:28:40 +00:00
return ! ! processed ;
2017-02-02 17:57:00 +00:00
}
2017-02-03 10:28:40 +00:00
return false ;
}
2017-03-09 12:49:18 +00:00
void DStaticEventHandler : : UiTick ( )
{
IFVIRTUAL ( DStaticEventHandler , UiTick )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2017-03-09 13:08:49 +00:00
VMValue params [ 1 ] = { ( DStaticEventHandler * ) this } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 1 , nullptr , 0 ) ;
2017-03-09 12:49:18 +00:00
}
}
2018-03-09 01:06:41 +00:00
void DStaticEventHandler : : PostUiTick ( )
{
IFVIRTUAL ( DStaticEventHandler , PostUiTick )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2018-03-09 01:06:41 +00:00
VMValue params [ 1 ] = { ( DStaticEventHandler * ) this } ;
VMCall ( func , params , 1 , nullptr , 0 ) ;
}
}
2017-03-06 09:25:30 +00:00
void DStaticEventHandler : : ConsoleProcess ( int player , FString name , int arg1 , int arg2 , int arg3 , bool manual )
2017-02-06 13:52:20 +00:00
{
2017-03-03 21:21:12 +00:00
if ( player < 0 )
2017-02-06 13:52:20 +00:00
{
2017-03-03 21:21:12 +00:00
IFVIRTUAL ( DStaticEventHandler , ConsoleProcess )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2017-03-06 21:27:51 +00:00
FConsoleEvent e ;
2017-03-03 21:21:12 +00:00
//
2017-03-06 21:27:51 +00:00
e . Player = player ;
e . Name = name ;
e . Args [ 0 ] = arg1 ;
e . Args [ 1 ] = arg2 ;
e . Args [ 2 ] = arg3 ;
e . IsManual = manual ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-03-03 21:21:12 +00:00
}
}
else
{
IFVIRTUAL ( DStaticEventHandler , NetworkProcess )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2017-03-06 21:27:51 +00:00
FConsoleEvent e ;
2017-03-03 21:21:12 +00:00
//
2017-03-06 21:27:51 +00:00
e . Player = player ;
e . Name = name ;
e . Args [ 0 ] = arg1 ;
e . Args [ 1 ] = arg2 ;
e . Args [ 2 ] = arg3 ;
e . IsManual = manual ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2017-03-03 21:21:12 +00:00
}
2017-02-06 13:52:20 +00:00
}
}
2019-02-02 15:43:11 +00:00
void DStaticEventHandler : : CheckReplacement ( PClassActor * replacee , PClassActor * * replacement , bool * final )
2018-08-15 15:46:03 +00:00
{
IFVIRTUAL ( DStaticEventHandler , CheckReplacement )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2018-08-16 18:46:40 +00:00
FReplaceEvent e = { replacee , * replacement , * final } ;
2018-08-15 15:46:03 +00:00
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
VMCall ( func , params , 2 , nullptr , 0 ) ;
if ( e . Replacement ! = replacee ) // prevent infinite recursion
* replacement = e . Replacement ;
2018-08-16 18:46:40 +00:00
* final = e . IsFinal ;
2018-08-15 15:46:03 +00:00
}
}
2019-01-28 00:03:04 +00:00
void DStaticEventHandler : : CheckReplacee ( PClassActor * * replacee , PClassActor * replacement , bool * final )
{
IFVIRTUAL ( DStaticEventHandler , CheckReplacee )
{
// don't create excessive DObjects if not going to be processed anyway
if ( isEmpty ( func ) ) return ;
FReplacedEvent e = { * replacee , replacement , * final } ;
VMValue params [ 2 ] = { ( DStaticEventHandler * ) this , & e } ;
VMCall ( func , params , 2 , nullptr , 0 ) ;
if ( e . Replacee ! = replacement ) // prevent infinite recursion
* replacee = e . Replacee ;
* final = e . IsFinal ;
}
}
2018-09-15 02:57:07 +00:00
void DStaticEventHandler : : NewGame ( )
{
IFVIRTUAL ( DStaticEventHandler , NewGame )
{
// don't create excessive DObjects if not going to be processed anyway
2018-11-29 17:46:28 +00:00
if ( isEmpty ( func ) ) return ;
2018-09-15 02:57:07 +00:00
VMValue params [ 1 ] = { ( DStaticEventHandler * ) this } ;
VMCall ( func , params , 1 , nullptr , 0 ) ;
}
}
2017-02-03 10:28:40 +00:00
//
void DStaticEventHandler : : OnDestroy ( )
{
2019-04-06 07:06:41 +00:00
owner - > UnregisterHandler ( this ) ;
2017-02-03 10:28:40 +00:00
Super : : OnDestroy ( ) ;
}
2017-02-06 13:52:20 +00:00
// console stuff
// this is kinda like puke, except it distinguishes between local events and playsim events.
CCMD ( event )
{
int argc = argv . argc ( ) ;
if ( argc < 2 | | argc > 5 )
{
Printf ( " Usage: event <name> [arg1] [arg2] [arg3] \n " ) ;
}
else
{
int arg [ 3 ] = { 0 , 0 , 0 } ;
int argn = MIN < int > ( argc - 2 , countof ( arg ) ) ;
for ( int i = 0 ; i < argn ; i + + )
arg [ i ] = atoi ( argv [ 2 + i ] ) ;
// call locally
2019-02-02 15:43:11 +00:00
primaryLevel - > localEventManager - > Console ( - 1 , argv [ 1 ] , arg [ 0 ] , arg [ 1 ] , arg [ 2 ] , true ) ;
2017-02-06 13:52:20 +00:00
}
}
CCMD ( netevent )
{
2017-02-06 14:14:18 +00:00
if ( gamestate ! = GS_LEVEL /* && gamestate != GS_TITLELEVEL*/ ) // not sure if this should work in title level, but probably not, because this is for actual playing
{
2019-05-18 08:35:05 +00:00
DPrintf ( DMSG_SPAMMY , " netevent cannot be used outside of a map. \n " ) ;
2017-02-06 14:14:18 +00:00
return ;
}
2017-02-06 14:02:44 +00:00
int argc = argv . argc ( ) ;
2017-02-06 13:52:20 +00:00
2017-02-06 14:02:44 +00:00
if ( argc < 2 | | argc > 5 )
{
2017-02-06 14:14:18 +00:00
Printf ( " Usage: netevent <name> [arg1] [arg2] [arg3] \n " ) ;
2017-02-06 14:02:44 +00:00
}
else
{
int arg [ 3 ] = { 0 , 0 , 0 } ;
int argn = MIN < int > ( argc - 2 , countof ( arg ) ) ;
for ( int i = 0 ; i < argn ; i + + )
arg [ i ] = atoi ( argv [ 2 + i ] ) ;
// call networked
2019-02-02 15:43:11 +00:00
primaryLevel - > localEventManager - > SendNetworkEvent ( argv [ 1 ] , arg [ 0 ] , arg [ 1 ] , arg [ 2 ] , true ) ;
2017-02-06 14:02:44 +00:00
}
2017-02-14 20:13:31 +00:00
}