123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825 |
- /*!
- Model Factory.
- create the additional scenenodes for ( bullets, health... )
- Defines the Entities for Quake3
- */
- #include <irrlicht.h>
- #include "q3factory.h"
- #include "sound.h"
- using namespace irr;
- using namespace scene;
- using namespace gui;
- using namespace video;
- using namespace core;
- using namespace quake3;
- //! This list is based on the original quake3.
- static const SItemElement Quake3ItemElement [] = {
- { "item_health",
- {"models/powerups/health/medium_cross.md3",
- "models/powerups/health/medium_sphere.md3"},
- "sound/items/n_health.wav",
- "icons/iconh_yellow",
- "25 Health",
- 25,
- HEALTH,
- SUB_NONE,
- SPECIAL_SFX_BOUNCE | SPECIAL_SFX_ROTATE_1
- },
- { "item_health_large",
- "models/powerups/health/large_cross.md3",
- "models/powerups/health/large_sphere.md3",
- "sound/items/l_health.wav",
- "icons/iconh_red",
- "50 Health",
- 50,
- HEALTH,
- SUB_NONE,
- SPECIAL_SFX_BOUNCE | SPECIAL_SFX_ROTATE_1
- },
- {
- "item_health_mega",
- "models/powerups/health/mega_cross.md3",
- "models/powerups/health/mega_sphere.md3",
- "sound/items/m_health.wav",
- "icons/iconh_mega",
- "Mega Health",
- 100,
- HEALTH,
- SUB_NONE,
- SPECIAL_SFX_BOUNCE | SPECIAL_SFX_ROTATE_1
- },
- {
- "item_health_small",
- "models/powerups/health/small_cross.md3",
- "models/powerups/health/small_sphere.md3",
- "sound/items/s_health.wav",
- "icons/iconh_green",
- "5 Health",
- 5,
- HEALTH,
- SUB_NONE,
- SPECIAL_SFX_BOUNCE | SPECIAL_SFX_ROTATE_1
- },
- { "ammo_bullets",
- "models/powerups/ammo/machinegunam.md3",
- "",
- "sound/misc/am_pkup.wav",
- "icons/icona_machinegun",
- "Bullets",
- 50,
- AMMO,
- MACHINEGUN,
- SPECIAL_SFX_BOUNCE,
- },
- {
- "ammo_cells",
- "models/powerups/ammo/plasmaam.md3",
- "",
- "sound/misc/am_pkup.wav",
- "icons/icona_plasma",
- "Cells",
- 30,
- AMMO,
- PLASMAGUN,
- SPECIAL_SFX_BOUNCE
- },
- { "ammo_rockets",
- "models/powerups/ammo/rocketam.md3",
- "",
- "",
- "icons/icona_rocket",
- "Rockets",
- 5,
- AMMO,
- ROCKET_LAUNCHER,
- SPECIAL_SFX_ROTATE
- },
- {
- "ammo_shells",
- "models/powerups/ammo/shotgunam.md3",
- "",
- "sound/misc/am_pkup.wav",
- "icons/icona_shotgun",
- "Shells",
- 10,
- AMMO,
- SHOTGUN,
- SPECIAL_SFX_ROTATE
- },
- {
- "ammo_slugs",
- "models/powerups/ammo/railgunam.md3",
- "",
- "sound/misc/am_pkup.wav",
- "icons/icona_railgun",
- "Slugs",
- 10,
- AMMO,
- RAILGUN,
- SPECIAL_SFX_ROTATE
- },
- {
- "item_armor_body",
- "models/powerups/armor/armor_red.md3",
- "",
- "sound/misc/ar2_pkup.wav",
- "icons/iconr_red",
- "Heavy Armor",
- 100,
- ARMOR,
- SUB_NONE,
- SPECIAL_SFX_ROTATE
- },
- {
- "item_armor_combat",
- "models/powerups/armor/armor_yel.md3",
- "",
- "sound/misc/ar2_pkup.wav",
- "icons/iconr_yellow",
- "Armor",
- 50,
- ARMOR,
- SUB_NONE,
- SPECIAL_SFX_ROTATE
- },
- {
- "item_armor_shard",
- "models/powerups/armor/shard.md3",
- "",
- "sound/misc/ar1_pkup.wav",
- "icons/iconr_shard",
- "Armor Shared",
- 5,
- ARMOR,
- SUB_NONE,
- SPECIAL_SFX_ROTATE
- },
- {
- "weapon_gauntlet",
- "models/weapons2/gauntlet/gauntlet.md3",
- "",
- "sound/misc/w_pkup.wav",
- "icons/iconw_gauntlet",
- "Gauntlet",
- 0,
- WEAPON,
- GAUNTLET,
- SPECIAL_SFX_ROTATE
- },
- {
- "weapon_shotgun",
- "models/weapons2/shotgun/shotgun.md3",
- "",
- "sound/misc/w_pkup.wav",
- "icons/iconw_shotgun",
- "Shotgun",
- 10,
- WEAPON,
- SHOTGUN,
- SPECIAL_SFX_ROTATE
- },
- {
- "weapon_machinegun",
- "models/weapons2/machinegun/machinegun.md3",
- "",
- "sound/misc/w_pkup.wav",
- "icons/iconw_machinegun",
- "Machinegun",
- 40,
- WEAPON,
- MACHINEGUN,
- SPECIAL_SFX_ROTATE
- },
- {
- "weapon_grenadelauncher",
- "models/weapons2/grenadel/grenadel.md3",
- "",
- "sound/misc/w_pkup.wav",
- "icons/iconw_grenade",
- "Grenade Launcher",
- 10,
- WEAPON,
- GRENADE_LAUNCHER,
- SPECIAL_SFX_ROTATE
- },
- {
- "weapon_rocketlauncher",
- "models/weapons2/rocketl/rocketl.md3",
- "",
- "sound/misc/w_pkup.wav",
- "icons/iconw_rocket",
- "Rocket Launcher",
- 10,
- WEAPON,
- ROCKET_LAUNCHER,
- SPECIAL_SFX_ROTATE
- },
- {
- "weapon_lightning",
- "models/weapons2/lightning/lightning.md3",
- "",
- "sound/misc/w_pkup.wav",
- "icons/iconw_lightning",
- "Lightning Gun",
- 100,
- WEAPON,
- LIGHTNING,
- SPECIAL_SFX_ROTATE
- },
- {
- "weapon_railgun",
- "models/weapons2/railgun/railgun.md3",
- "",
- "sound/misc/w_pkup.wav",
- "icons/iconw_railgun",
- "Railgun",
- 10,
- WEAPON,
- RAILGUN,
- SPECIAL_SFX_ROTATE
- },
- {
- "weapon_plasmagun",
- "models/weapons2/plasma/plasma.md3",
- "",
- "sound/misc/w_pkup.wav",
- "icons/iconw_plasma",
- "Plasma Gun",
- 50,
- WEAPON,
- PLASMAGUN,
- SPECIAL_SFX_ROTATE
- },
- {
- "weapon_bfg",
- "models/weapons2/bfg/bfg.md3",
- "",
- "sound/misc/w_pkup.wav",
- "icons/iconw_bfg",
- "BFG10K",
- 20,
- WEAPON,
- BFG,
- SPECIAL_SFX_ROTATE
- },
- {
- "weapon_grapplinghook",
- "models/weapons2/grapple/grapple.md3",
- "",
- "sound/misc/w_pkup.wav",
- "icons/iconw_grapple",
- "Grappling Hook",
- 0,
- WEAPON,
- GRAPPLING_HOOK,
- SPECIAL_SFX_ROTATE
- },
- {
- 0
- }
- };
- /*!
- */
- const SItemElement * getItemElement ( const stringc& key )
- {
- const SItemElement *item = Quake3ItemElement;
- while ( item->key )
- {
- if ( 0 == strcmp ( key.c_str(), item->key ) )
- return item;
- item += 1;
- }
- return 0;
- }
- /*!
- Quake3 model factory.
- Takes the mesh buffers and creates scenenodes for their associated shaders
- */
- void Q3ShaderFactory ( Q3LevelLoadParameter &loadParam,
- IrrlichtDevice *device,
- IQ3LevelMesh* mesh,
- eQ3MeshIndex meshIndex,
- ISceneNode *parent,
- IMetaTriangleSelector *meta,
- bool showShaderName )
- {
- if ( 0 == mesh || 0 == device )
- return;
- IMeshSceneNode* node = 0;
- ISceneManager* smgr = device->getSceneManager();
- ITriangleSelector * selector = 0;
- // the additional mesh can be quite huge and is unoptimized
- // Save to cast to SMesh
- SMesh * additional_mesh = (SMesh*) mesh->getMesh ( meshIndex );
- if ( 0 == additional_mesh || additional_mesh->getMeshBufferCount() == 0)
- return;
- char buf[128];
- if ( loadParam.verbose > 0 )
- {
- loadParam.startTime = device->getTimer()->getRealTime();
- if ( loadParam.verbose > 1 )
- {
- snprintf_irr(buf, 128, "q3shaderfactory start" );
- device->getLogger()->log( buf, ELL_INFORMATION);
- }
- }
- IGUIFont *font = 0;
- if ( showShaderName )
- font = device->getGUIEnvironment()->getFont("fontlucida.png");
- IVideoDriver *driver = device->getVideoDriver();
- // create helper textures
- if ( 1 )
- {
- tTexArray tex;
- u32 pos = 0;
- getTextures ( tex, "$redimage $blueimage $whiteimage $checkerimage", pos,
- device->getFileSystem(), driver );
- }
- s32 sceneNodeID = 0;
- for ( u32 i = 0; i!= additional_mesh->getMeshBufferCount (); ++i )
- {
- IMeshBuffer *meshBuffer = additional_mesh->getMeshBuffer ( i );
- const SMaterial &material = meshBuffer->getMaterial();
- //! The ShaderIndex is stored in the second material parameter
- s32 shaderIndex = (s32) material.MaterialTypeParam2;
- // the meshbuffer can be rendered without additional support, or it has no shader
- IShader *shader = (IShader *) mesh->getShader ( shaderIndex );
- // no shader, or mapped to existing material
- if ( 0 == shader )
- {
- #if 1
- // clone mesh
- SMesh * m = new SMesh ();
- m->addMeshBuffer ( meshBuffer );
- SMaterial &mat = m->getMeshBuffer( 0 )->getMaterial();
- if ( mat.getTexture( 0 ) == 0 )
- mat.setTexture ( 0, driver->getTexture ( "$blueimage" ) );
- if ( mat.getTexture( 1 ) == 0 )
- mat.setTexture ( 1, driver->getTexture ( "$redimage" ) );
- IMesh * store = smgr->getMeshManipulator ()->createMeshWith2TCoords ( m );
- m->drop();
- node = smgr->addMeshSceneNode ( store, parent, sceneNodeID );
- node->setAutomaticCulling ( scene::EAC_OFF );
- store->drop ();
- sceneNodeID += 1;
- #endif
- }
- else if ( 1 )
- {
- /*
- stringc s;
- dumpShader ( s, shader );
- printf ( s.c_str () );
- */
- // create sceneNode
- node = smgr->addQuake3SceneNode ( meshBuffer, shader, parent, sceneNodeID );
- node->setAutomaticCulling ( scene::EAC_FRUSTUM_BOX );
- sceneNodeID += 1;
- }
- // show debug shader name
- if ( showShaderName && node )
- {
- swprintf_irr ( (wchar_t*) buf, 64, L"%hs:%d", node->getName(),node->getID() );
- smgr->addBillboardTextSceneNode(
- font,
- (wchar_t*) buf,
- node,
- dimension2d<f32>(80.0f, 8.0f),
- vector3df(0, 10, 0),
- sceneNodeID);
- sceneNodeID += 1;
- }
- // create portal rendertargets
- if ( shader )
- {
- const SVarGroup *group = shader->getGroup(1);
- if ( group->isDefined( "surfaceparm", "portal" ) )
- {
- }
- }
- // add collision
- // find out if shader is marked as nonsolid
- u8 doCreate = meta !=0 ;
- if ( shader )
- {
- const SVarGroup *group = shader->getGroup(1);
- if ( group->isDefined( "surfaceparm", "trans" )
- // || group->isDefined( "surfaceparm", "sky" )
- // || group->isDefined( "surfaceparm", "nonsolid" )
- )
- {
- if ( !group->isDefined( "surfaceparm", "metalsteps" ) )
- {
- doCreate = 0;
- }
- }
- }
- if ( doCreate )
- {
- IMesh *m = 0;
- //! controls if triangles are modified by the scenenode during runtime
- bool takeOriginal = true;
- if ( takeOriginal )
- {
- m = new SMesh ();
- ((SMesh*) m )->addMeshBuffer (meshBuffer);
- }
- else
- {
- m = node->getMesh();
- }
- //selector = smgr->createOctreeTriangleSelector ( m, 0, 128 );
- selector = smgr->createTriangleSelector ( m, 0 );
- meta->addTriangleSelector ( selector );
- selector->drop ();
- if ( takeOriginal )
- {
- delete m;
- }
- }
- }
- #if 0
- if ( meta )
- {
- selector = smgr->createOctreeTriangleSelector ( additional_mesh, 0 );
- meta->addTriangleSelector ( selector );
- selector->drop ();
- }
- #endif
- if ( loadParam.verbose > 0 )
- {
- loadParam.endTime = device->getTimer()->getRealTime ();
- snprintf_irr(buf, 128, "q3shaderfactory needed %04d ms to create %d shader nodes",
- loadParam.endTime - loadParam.startTime,
- sceneNodeID
- );
- device->getLogger()->log(buf, ELL_INFORMATION);
- }
- }
- /*!
- create items from entity
- */
- void Q3ModelFactory ( Q3LevelLoadParameter &loadParam,
- IrrlichtDevice *device,
- IQ3LevelMesh* masterMesh,
- ISceneNode *parent,
- bool showShaderName
- )
- {
- if ( 0 == masterMesh )
- return;
- tQ3EntityList &entity = masterMesh->getEntityList ();
- ISceneManager* smgr = device->getSceneManager();
- char buf[128];
- const SVarGroup *group = 0;
- IEntity search;
- s32 index;
- s32 lastIndex;
- /*
- stringc s;
- FILE *f = 0;
- f = fopen ( "entity.txt", "wb" );
- for ( index = 0; (u32) index < entityList.size (); ++index )
- {
- const IEntity *entity = &entityList[ index ];
- s = entity->name;
- dumpShader ( s, entity );
- fwrite ( s.c_str(), 1, s.size(), f );
- }
- fclose ( f );
- */
- IAnimatedMeshMD3* model = 0;
- SMD3Mesh * mesh = 0;
- const SMD3MeshBuffer *meshBuffer = 0;
- IMeshSceneNode* node = 0;
- ISceneNodeAnimator* anim = 0;
- const IShader *shader = 0;
- u32 pos;
- vector3df p;
- u32 nodeCount = 0;
- tTexArray textureArray;
- IGUIFont *font = 0;
- if ( showShaderName )
- font = device->getGUIEnvironment()->getFont("fontlucida.png");
- const SItemElement *itemElement = 0;
- // walk list
- for ( index = 0; (u32) index < entity.size(); ++index )
- {
- itemElement = getItemElement ( entity[index].name );
- if ( 0 == itemElement )
- continue;
- pos = 0;
- p = getAsVector3df ( entity[index].getGroup(1)->get ( "origin" ), pos );
- nodeCount += 1;
- for ( u32 g = 0; g < 2; ++g )
- {
- if ( 0 == itemElement->model[g] || itemElement->model[g][0] == 0 )
- continue;
- model = (IAnimatedMeshMD3*) smgr->getMesh( itemElement->model[g] );
- if ( 0 == model )
- continue;
- mesh = model->getOriginalMesh();
- for ( u32 j = 0; j != mesh->Buffer.size (); ++j )
- {
- meshBuffer = mesh->Buffer[j];
- if ( 0 == meshBuffer )
- continue;
- shader = masterMesh->getShader ( meshBuffer->Shader.c_str(), false );
- IMeshBuffer *final = model->getMesh(0)->getMeshBuffer(j);
- if ( shader )
- {
- //!TODO: Hack don't modify the vertexbuffer. make it better;-)
- final->getMaterial().ColorMask = 0;
- node = smgr->addQuake3SceneNode ( final, shader, parent );
- final->getMaterial().ColorMask = 15;
- }
- else
- {
- // clone mesh
- SMesh * m = new SMesh ();
- m->addMeshBuffer ( final );
- node = smgr->addMeshSceneNode ( m, parent );
- m->drop();
- }
- if ( 0 == node )
- {
- snprintf_irr ( buf, 128, "q3ModelFactory shader %s failed", meshBuffer->Shader.c_str() );
- device->getLogger()->log ( buf );
- continue;
- }
- // node was maybe centered by shaderscenenode
- node->setPosition ( p );
- node->setName ( meshBuffer->Shader );
- node->setAutomaticCulling ( scene::EAC_BOX );
- // add special effects to node
- if ( itemElement->special & SPECIAL_SFX_ROTATE ||
- (g == 0 && itemElement->special & SPECIAL_SFX_ROTATE_1)
- )
- {
- anim = smgr->createRotationAnimator ( vector3df ( 0.f,
- 2.f, 0.f ) );
- node->addAnimator ( anim );
- anim->drop ();
- }
- if ( itemElement->special & SPECIAL_SFX_BOUNCE )
- {
- //anim = smgr->createFlyStraightAnimator (
- // p, p + vector3df ( 0.f, 60.f, 0.f ), 1000, true, true );
- anim = smgr->createFlyCircleAnimator (
- p + vector3df( 0.f, 20.f, 0.f ),
- 20.f,
- 0.005f,
- vector3df ( 1.f, 0.f, 0.f ),
- core::fract ( nodeCount * 0.05f ),
- 1.f
- );
- node->addAnimator ( anim );
- anim->drop ();
- }
- }
- }
- // show name
- if ( showShaderName )
- {
- swprintf_irr ( (wchar_t*) buf, sizeof(buf) / 2, L"%hs", itemElement->key );
- smgr->addBillboardTextSceneNode(
- font,
- (wchar_t*) buf,
- parent,
- dimension2d<f32>(80.0f, 8.0f),
- p + vector3df(0, 30, 0),
- 0);
- }
- }
- // music
- search.name = "worldspawn";
- index = entity.binary_search_multi ( search, lastIndex );
- if ( index >= 0 )
- {
- group = entity[ index ].getGroup(1);
- background_music ( group->get ( "music" ).c_str () );
- }
- // music
- search.name = "worldspawn";
- index = entity.binary_search_multi ( search, lastIndex );
- if ( index >= 0 )
- {
- group = entity[ index ].getGroup(1);
- background_music ( group->get ( "music" ).c_str () );
- }
- //IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");
- //IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
- }
- /*!
- so we need a good starting Position in the level.
- we can ask the Quake3 Loader for all entities with class_name "info_player_deathmatch"
- */
- s32 Q3StartPosition ( IQ3LevelMesh* mesh,
- ICameraSceneNode* camera,
- s32 startposIndex,
- const vector3df &translation
- )
- {
- if ( 0 == mesh )
- return 0;
- tQ3EntityList &entityList = mesh->getEntityList ();
- IEntity search;
- search.name = "info_player_start"; // "info_player_deathmatch";
- // find all entities in the multi-list
- s32 lastIndex;
- s32 index = entityList.binary_search_multi ( search, lastIndex );
- if ( index < 0 )
- {
- search.name = "info_player_deathmatch";
- index = entityList.binary_search_multi ( search, lastIndex );
- }
- if ( index < 0 )
- return 0;
- index += core::clamp ( startposIndex, 0, lastIndex - index );
- u32 parsepos;
- const SVarGroup *group = 0;
- group = entityList[ index ].getGroup(1);
- parsepos = 0;
- vector3df pos = getAsVector3df ( group->get ( "origin" ), parsepos );
- pos += translation;
- parsepos = 0;
- f32 angle = getAsFloat ( group->get ( "angle"), parsepos );
- vector3df target ( 0.f, 0.f, 1.f );
- target.rotateXZBy ( angle - 90.f, vector3df () );
- if ( camera )
- {
- camera->setPosition ( pos );
- camera->setTarget ( pos + target );
- //! New. FPSCamera and animators catches reset on animate 0
- camera->OnAnimate ( 0 );
- }
- return lastIndex - index + 1;
- }
- /*!
- gets a accumulated force on a given surface
- */
- vector3df getGravity ( const c8 * surface )
- {
- if ( 0 == strcmp ( surface, "earth" ) ) return vector3df ( 0.f, -900.f, 0.f );
- if ( 0 == strcmp ( surface, "moon" ) ) return vector3df ( 0.f, -6.f , 0.f );
- if ( 0 == strcmp ( surface, "water" ) ) return vector3df ( 0.1f, -2.f, 0.f );
- if ( 0 == strcmp ( surface, "ice" ) ) return vector3df ( 0.2f, -9.f, 0.3f );
- return vector3df ( 0.f, 0.f, 0.f );
- }
- /*
- Dynamically load the Irrlicht Library
- */
- #if defined(_IRR_WINDOWS_API_)
- #ifdef _MSC_VER
- #pragma comment(lib, "Irrlicht.lib")
- #endif
- #include <windows.h>
- funcptr_createDevice load_createDevice ( const c8 * filename)
- {
- return (funcptr_createDevice) GetProcAddress ( LoadLibrary ( filename ), "createDevice" );
- }
- funcptr_createDeviceEx load_createDeviceEx ( const c8 * filename)
- {
- return (funcptr_createDeviceEx) GetProcAddress ( LoadLibrary ( filename ), "createDeviceEx" );
- }
- #else
- // TODO: Dynamic Loading for other os
- funcptr_createDevice load_createDevice ( const c8 * filename)
- {
- return createDevice;
- }
- funcptr_createDeviceEx load_createDeviceEx ( const c8 * filename)
- {
- return createDeviceEx;
- }
- #endif
- /*
- get the current collision response camera animator
- */
- ISceneNodeAnimatorCollisionResponse* camCollisionResponse( IrrlichtDevice * device )
- {
- ICameraSceneNode *camera = device->getSceneManager()->getActiveCamera();
- ISceneNodeAnimatorCollisionResponse *a = 0;
- list<ISceneNodeAnimator*>::ConstIterator it = camera->getAnimators().begin();
- for (; it != camera->getAnimators().end(); ++it)
- {
- a = (ISceneNodeAnimatorCollisionResponse*) (*it);
- if ( a->getType() == ESNAT_COLLISION_RESPONSE )
- return a;
- }
- return 0;
- }
- //! internal animation
- void setTimeFire ( TimeFire *t, u32 delta, u32 flags )
- {
- t->flags = flags;
- t->next = 0;
- t->delta = delta;
- }
- void checkTimeFire ( TimeFire *t, u32 listSize, u32 now )
- {
- u32 i;
- for ( i = 0; i < listSize; ++i )
- {
- if ( now < t[i].next )
- continue;
- t[i].next = core::max_ ( now + t[i].delta, t[i].next + t[i].delta );
- t[i].flags |= FIRED;
- }
- }
|