From 00fa45161da7b5433d27b394e26971513fd2acd6 Mon Sep 17 00:00:00 2001 From: Spahbod Date: Mon, 30 Jun 2014 13:59:34 +0000 Subject: [PATCH] Added "Survival of the Fittest", a demo random map with triggers. Refs #52 This was SVN commit r15468. --- .../maps/random/rmgen/utilityfunctions.js | 4 +- .../maps/random/survivalofthefittest.js | 296 ++++++++++++++++++ .../maps/random/survivalofthefittest.json | 16 + .../random/survivalofthefittest_triggers.js | 171 ++++++++++ .../mods/public/maps/scripts/TriggerHelper.js | 36 ++- 5 files changed, 520 insertions(+), 3 deletions(-) create mode 100644 binaries/data/mods/public/maps/random/survivalofthefittest.js create mode 100644 binaries/data/mods/public/maps/random/survivalofthefittest.json create mode 100644 binaries/data/mods/public/maps/random/survivalofthefittest_triggers.js diff --git a/binaries/data/mods/public/maps/random/rmgen/utilityfunctions.js b/binaries/data/mods/public/maps/random/rmgen/utilityfunctions.js index 8bde2b94b0..88d324c486 100644 --- a/binaries/data/mods/public/maps/random/rmgen/utilityfunctions.js +++ b/binaries/data/mods/public/maps/random/rmgen/utilityfunctions.js @@ -130,8 +130,8 @@ function createForests(terrainset, constraint, tileclass, numMultiplier, biomeID var num = floor(size / types.length); for (var i = 0; i < types.length; ++i) { - placer = new ChainPlacer(1, floor(scaleByMapSize(3, 5)), numForest / num, 0.5); - painter = new LayeredPainter( + var placer = new ChainPlacer(1, floor(scaleByMapSize(3, 5)), numForest / num, 0.5); + var painter = new LayeredPainter( types[i], // terrains [2] // widths ); diff --git a/binaries/data/mods/public/maps/random/survivalofthefittest.js b/binaries/data/mods/public/maps/random/survivalofthefittest.js new file mode 100644 index 0000000000..c53317bde2 --- /dev/null +++ b/binaries/data/mods/public/maps/random/survivalofthefittest.js @@ -0,0 +1,296 @@ +RMS.LoadLibrary("rmgen"); + +//random terrain textures +var random_terrain = randomizeBiome(); + +const tMainTerrain = rBiomeT1(); +const tForestFloor1 = rBiomeT2(); +const tForestFloor2 = rBiomeT3(); +const tCliff = rBiomeT4(); +const tTier1Terrain = rBiomeT5(); +const tTier2Terrain = rBiomeT6(); +const tTier3Terrain = rBiomeT7(); +const tHill = rBiomeT8(); +const tDirt = rBiomeT9(); +const tRoad = rBiomeT10(); +const tRoadWild = rBiomeT11(); +const tTier4Terrain = rBiomeT12(); +const tShoreBlend = rBiomeT13(); +const tShore = rBiomeT14(); +const tWater = rBiomeT15(); + +// gaia entities +const oTree1 = rBiomeE1(); +const oTree2 = rBiomeE2(); +const oTree3 = rBiomeE3(); +const oTree4 = rBiomeE4(); +const oTree5 = rBiomeE5(); +const oFruitBush = rBiomeE6(); +const oChicken = rBiomeE7(); +const oMainHuntableAnimal = rBiomeE8(); +const oFish = rBiomeE9(); +const oSecondaryHuntableAnimal = rBiomeE10(); +const oStoneLarge = rBiomeE11(); +const oStoneSmall = rBiomeE12(); +const oMetalLarge = rBiomeE13(); +const oWood = "gaia/special_treasure_wood"; +const oFood = "gaia/special_treasure_food_bin"; + +// decorative props +const aGrass = rBiomeA1(); +const aGrassShort = rBiomeA2(); +const aReeds = rBiomeA3(); +const aLillies = rBiomeA4(); +const aRockLarge = rBiomeA5(); +const aRockMedium = rBiomeA6(); +const aBushMedium = rBiomeA7(); +const aBushSmall = rBiomeA8(); +const aTree = rBiomeA9(); + +const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; +const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; +const BUILDING_ANGlE = -PI/4; + +// initialize map + +log("Initializing map..."); + +InitMap(); + +var numPlayers = getNumPlayers() - 1; +var mapSize = getMapSize(); +var mapArea = mapSize*mapSize; + +// create tile classes + +var clPlayer = createTileClass(); +var clHill = createTileClass(); +var clHill2 = createTileClass(); +var clForest = createTileClass(); +var clWater = createTileClass(); +var clDirt = createTileClass(); +var clRock = createTileClass(); +var clMetal = createTileClass(); +var clFood = createTileClass(); +var clBaseResource = createTileClass(); +var clSettlement = createTileClass(); +var clLand = createTileClass(); +var clWomen = createTileClass(); + +for (var ix = 0; ix < mapSize; ix++) +{ + for (var iz = 0; iz < mapSize; iz++) + { + var x = ix / (mapSize + 1.0); + var z = iz / (mapSize + 1.0); + placeTerrain(ix, iz, tMainTerrain); + } +} + +var fx = fractionToTiles(0.5); +var fz = fractionToTiles(0.5); +ix = round(fx); +iz = round(fz); + +var lSize = sqrt(sqrt(sqrt(scaleByMapSize(1, 6)))); + +var placer = new ClumpPlacer(mapArea * 0.065 * lSize, 0.7, 0.1, 10, ix, iz); +var terrainPainter = new LayeredPainter( + [tMainTerrain, tMainTerrain], // terrains + [3] // widths +); +var elevationPainter = new SmoothElevationPainter( + ELEVATION_SET, // type + 3, // elevation + 3 // blend radius +); +createArea(placer, [terrainPainter, elevationPainter, paintClass(clLand)], null); + +// randomize player order +var playerIDs = []; +for (var i = 0; i < numPlayers; i++) +{ + playerIDs.push(i+1); +} +playerIDs = sortPlayers(playerIDs); + +// place players + +var playerX = new Array(numPlayers); +var playerZ = new Array(numPlayers); +var attackerX = new Array(numPlayers); +var attackerZ = new Array(numPlayers); +var playerAngle = new Array(numPlayers); + +var startAngle = randFloat(0, TWO_PI); +for (var i = 0; i < numPlayers; i++) +{ + playerAngle[i] = startAngle + i*TWO_PI/numPlayers; + playerX[i] = 0.5 + 0.3*cos(playerAngle[i]); + playerZ[i] = 0.5 + 0.3*sin(playerAngle[i]); + attackerX[i] = 0.5 + 0.45*cos(playerAngle[i]); + attackerZ[i] = 0.5 + 0.45*sin(playerAngle[i]); +} + +// determines the trigger point associated with the attacker spawning point of each player. +var attackerTriggerPointForPlayer = ["", "B", "C", "D", "E", "F", "G", "H"]; + +for (var i = 0; i < numPlayers; i++) +{ + var id = playerIDs[i]; + log("Creating base for player " + id + "..."); + + // some constants + var radius = scaleByMapSize(15,25); + var cliffRadius = 2; + var elevation = 20; + + // place the attacker spawning trigger point + var ax = round(fractionToTiles(attackerX[i])); + var az = round(fractionToTiles(attackerZ[i])); + placeObject(ax, az, "special/trigger_point_" + attackerTriggerPointForPlayer[id], 0, PI); + addToClass(ax, az, clPlayer); + addToClass(round(fractionToTiles((attackerX[i] + playerX[i]) / 2)), round(fractionToTiles((attackerZ[i] + playerZ[i]) / 2)), clPlayer); + + // get the x and z in tiles + fx = fractionToTiles(playerX[i]); + fz = fractionToTiles(playerZ[i]); + ix = round(fx); + iz = round(fz); + addToClass(ix, iz, clPlayer); + addToClass(ix+5, iz, clPlayer); + addToClass(ix, iz+5, clPlayer); + addToClass(ix-5, iz, clPlayer); + addToClass(ix, iz-5, clPlayer); + + // Place default civ starting entities + var uDist = 6; + var uSpace = 2; + placeObject(fx, fz, "skirmish/structures/default_civil_centre", id, BUILDING_ANGlE); + var uAngle = BUILDING_ANGlE - PI / 2; + var count = 4; + for (var numberofentities = 0; numberofentities < count; numberofentities++) + { + var ux = fx + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2)); + var uz = fz + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2)); + placeObject(ux, uz, "skirmish/units/default_infantry_melee_b", id, uAngle); + } + + // create grass tufts + var num = radius * radius * 3.14 / 250; + for (var j = 0; j < num; j++) + { + var gAngle = randFloat(0, TWO_PI); + var gDist = radius - (5 + randInt(7)); + var gX = round(fx + gDist * cos(gAngle)); + var gZ = round(fz + gDist * sin(gAngle)); + var group = new SimpleGroup( + [new SimpleObject(aGrassShort, 2,5, 0,1, -PI/8,PI/8)], + false, clBaseResource, gX, gZ + ); + createObjectGroup(group, 0); + } + + var tang = startAngle + (i+0.5)*TWO_PI/numPlayers; + var placer = new PathPlacer(fractionToTiles(0.5), fractionToTiles(0.5), fractionToTiles(0.5 + 0.5*cos(tang)), fractionToTiles(0.5 + 0.5*sin(tang)), scaleByMapSize(14,24), 0.4, 3*(scaleByMapSize(1,3)), 0.2, 0.05); + var terrainPainter = new LayeredPainter( + [tMainTerrain, tMainTerrain], // terrains + [1] // widths + ); + var elevationPainter = new SmoothElevationPainter( + ELEVATION_SET, // type + 3, // elevation + 4 // blend radius + ); + createArea(placer, [terrainPainter, elevationPainter, paintClass(clWater)], null); + + //creating female citizens + var femaleLocation = getTIPIADBON([ix, iz], [mapSize / 2, mapSize / 2], [-3 , 3.5], 1, 3); + if (femaleLocation !== undefined) + { + placeObject(femaleLocation[0], femaleLocation[1], "skirmish/units/default_support_female_citizen", id, playerAngle[i] + PI); + addToClass(floor(femaleLocation[0]), floor(femaleLocation[1]), clWomen); + } +} + +paintTerrainBasedOnHeight(3.12, 29, 1, tCliff); +paintTileClassBasedOnHeight(3.12, 29, 1, clHill); + +// create trigger points for treasures +group = new SimpleGroup( [new SimpleObject("special/trigger_point_A", 1,1, 0,0)], true, clWomen); +createObjectGroups(group, 0, + [avoidClasses(clForest, 5, clPlayer, 5, clHill, 5), stayClasses(clLand, 5)], + scaleByMapSize(40, 140), 100 +); + + +// create bumps +createBumps([avoidClasses(clWater, 2, clPlayer, 10), stayClasses(clLand, 5)]); + +// create hills +if (randInt(1,2) == 1) + createHills([tMainTerrain, tCliff, tHill], [avoidClasses(clPlayer, 20, clHill, 5, clBaseResource, 3, clWomen, 5), stayClasses(clLand, 5)], clHill, scaleByMapSize(10, 60) * numPlayers); +else + createMountains(tCliff, [avoidClasses(clPlayer, 20, clHill, 5, clBaseResource, 3, clWomen, 5), stayClasses(clLand, 5)], clHill, scaleByMapSize(10, 60) * numPlayers); +createHills([tCliff, tCliff, tHill], avoidClasses(clPlayer, 20, clHill, 5, clBaseResource, 3, clWomen, 5, clLand, 5), clHill, scaleByMapSize(15, 90) * numPlayers, undefined, undefined, undefined, undefined, 55); + +// create forests +createForests( + [tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2], + [avoidClasses(clPlayer, 20, clForest, 5, clHill, 0, clBaseResource,2, clWomen, 5), stayClasses(clLand, 4)], + clForest, + 1.0, + random_terrain +); + +RMS.SetProgress(50); + +// create dirt patches +log("Creating dirt patches..."); +createLayeredPatches( + [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], + [[tMainTerrain,tTier1Terrain],[tTier1Terrain,tTier2Terrain], [tTier2Terrain,tTier3Terrain]], + [1,1], + [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12, clWomen, 5), stayClasses(clLand, 5)] +); + +// create grass patches +log("Creating grass patches..."); +createPatches( + [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)], + tTier4Terrain, + [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12, clWomen, 5), stayClasses(clLand, 5)] +); + +// create decoration +var planetm = 1; + +if (random_terrain==7) + planetm = 8; + +createDecoration +( + [[new SimpleObject(aRockMedium, 1,3, 0,1)], + [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)], + [new SimpleObject(aGrassShort, 1,2, 0,1, -PI/8,PI/8)], + [new SimpleObject(aGrass, 2,4, 0,1.8, -PI/8,PI/8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -PI/8,PI/8)], + [new SimpleObject(aBushMedium, 1,2, 0,2), new SimpleObject(aBushSmall, 2,4, 0,2)] + ], + [ + scaleByMapSize(16, 262), + scaleByMapSize(8, 131), + planetm * scaleByMapSize(13, 200), + planetm * scaleByMapSize(13, 200), + planetm * scaleByMapSize(13, 200) + ], + [avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 5)] +); + +// create straggler trees +log("Creating straggler trees..."); +var types = [oTree1, oTree2, oTree4, oTree3]; // some variation +createStragglerTrees(types, [avoidClasses(clForest, 7, clHill, 1, clPlayer, 9, clMetal, 1, clRock, 1), stayClasses(clLand, 7)]); + + +// Export map data +ExportMap(); \ No newline at end of file diff --git a/binaries/data/mods/public/maps/random/survivalofthefittest.json b/binaries/data/mods/public/maps/random/survivalofthefittest.json new file mode 100644 index 0000000000..1e52c7ab03 --- /dev/null +++ b/binaries/data/mods/public/maps/random/survivalofthefittest.json @@ -0,0 +1,16 @@ +{ + "settings" : { + "Name" : "Survival of the Fittest", + "Script" : "survivalofthefittest.js", + "Description" : "IMPORTANT NOTE: SET THE LAST PLAYER TO UNDEFINED AI.\n\nAI PLAYERS WONT WORK WITH THIS MAP\n\nProtect your base against endless waves of enemies. Use your woman citizen to gather resources. The last player remaining will be the winner.", + "BaseTerrain" : ["medit_sea_depths"], + "BaseHeight" : 30, + "CircularMap" : true, + "Keywords": ["demo"], + "TriggerScripts": [ + "scripts/TriggerHelper.js", + "random/survivalofthefittest_triggers.js" + ], + "XXXXXX" : "Optionally define other things here, like we would for a scenario" + } +} \ No newline at end of file diff --git a/binaries/data/mods/public/maps/random/survivalofthefittest_triggers.js b/binaries/data/mods/public/maps/random/survivalofthefittest_triggers.js new file mode 100644 index 0000000000..1bb8a99fa6 --- /dev/null +++ b/binaries/data/mods/public/maps/random/survivalofthefittest_triggers.js @@ -0,0 +1,171 @@ +var attackerTriggerPointForPlayer = ["", "B", "C", "D", "E", "F", "G", "H"]; +var numberOfPlayers = TriggerHelper.GetNumberOfPlayers() - 1; +var treasures = ["gaia/special_treasure_food_barrel", + "gaia/special_treasure_food_bin", + "gaia/special_treasure_food_crate", + "gaia/special_treasure_food_jars", + "gaia/special_treasure_metal", + "gaia/special_treasure_stone", + "gaia/special_treasure_wood", + "gaia/special_treasure_wood", + "gaia/special_treasure_wood"]; +var attackerEntityTemplates = ["units/athen_champion_infantry", + "units/athen_champion_marine", + "units/athen_champion_ranged", + "units/brit_champion_cavalry", + "units/brit_champion_infantry", + "units/cart_champion_cavalry", + "units/cart_champion_elephant", + "units/cart_champion_infantry", + "units/cart_champion_pikeman", + "units/gaul_champion_cavalry", + "units/gaul_champion_fanatic", + "units/gaul_champion_infantry", + "units/iber_champion_cavalry", + "units/iber_champion_infantry", + "units/mace_champion_cavalry", + "units/mace_champion_infantry_a", + "units/mace_champion_infantry_e", + "units/maur_champion_chariot", + "units/maur_champion_elephant", + "units/maur_champion_infantry", + "units/maur_champion_maiden", + "units/maur_champion_maiden_archer", + "units/pers_champion_cavalry", + "units/pers_champion_infantry", + "units/ptol_champion_cavalry", + "units/ptol_champion_elephant", + "units/rome_champion_cavalry", + "units/rome_champion_infantry", + "units/sele_champion_cavalry", + "units/sele_champion_chariot", + "units/sele_champion_elephant", + "units/sele_champion_infantry_pikeman", + "units/sele_champion_infantry_swordsman", + "units/spart_champion_infantry_pike", + "units/spart_champion_infantry_spear", + "units/spart_champion_infantry_sword"]; + +Trigger.prototype.StartAnEnemyWave = function() +{ + var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + var attackerEntity = attackerEntityTemplates[Math.floor(Math.random() * attackerEntityTemplates.length)]; + var attackerCount = cmpTimer.GetTime() / 180000; // A soldier for each 3 minutes of the game. Should be waves of 20 soldiers after an hour + + for (var i = 1; i < numberOfPlayers; ++i) + { + if (TriggerHelper.GetPlayerComponent(i).GetState() == "active") // If the player isn't yet defeated + { + // spawn attackers + var attackers = TriggerHelper.SpawnUnitsFromTriggerPoints(attackerTriggerPointForPlayer[i], attackerEntity, attackerCount, numberOfPlayers); + + // order them to attack + for (var attacker in attackers) + { + var cmpPosition = Engine.QueryInterface(cmpTrigger.playerCivicCenter[i], IID_Position); + if (!cmpPosition || !cmpPosition.IsInWorld) + break; + // store the x and z coordinates in the command + var cmd = cmpPosition.GetPosition(); + cmd.type = "attack-walk"; + cmd.entities = attackers[attacker]; + cmd.queued = true; + ProcessCommand(numberOfPlayers, cmd); + } + } + } + + var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); + cmpGUIInterface.PushNotification({ + "players": [1,2,3,4,5,6,7,8], + "message": markForTranslation("An enemy wave is attacking!"), + "translateMessage": true + }); + cmpTrigger.DoAfterDelay(180000, "StartAnEnemyWave", {}); // The next wave will come in 3 minutes +} + +Trigger.prototype.InitGame = function() +{ + // Find all of the civic centers + for (var i = 1; i < numberOfPlayers; ++i) + { + var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); + var playerEntities = cmpRangeManager.GetEntitiesByPlayer(i); // Get all of each player's entities + + for each (var entity in playerEntities) + { + if (TriggerHelper.EntityHasClass(entity, "CivilCentre")) + { + cmpTrigger.playerCivicCenter[i] = entity; + } + } + } + + // Fix alliances + for (var i = 1; i < numberOfPlayers; ++i) + { + var cmpPlayer = TriggerHelper.GetPlayerComponent(i); + for (var j = 1; j < numberOfPlayers; ++j) + if (i != j) + cmpPlayer.SetAlly(j); + cmpPlayer.SetEnemy(numberOfPlayers); + cmpPlayer.SetLockTeams(true); + } + + // Place the treasures + cmpTrigger.DoAction({action: "PlaceTreasures"}); + + // Additional stuff + var cmpPlayer = TriggerHelper.GetPlayerComponent(numberOfPlayers); + cmpPlayer.SetName("Enemy Waves"); +} + +Trigger.prototype.PlaceTreasures = function() +{ + var triggerPoints = cmpTrigger.GetTriggerPoints("A"); + for (var point of triggerPoints) + { + var template = treasures[Math.floor(Math.random() * treasures.length)] + TriggerHelper.SpawnUnits(point, template, 1, 0); + } + cmpTrigger.DoAfterDelay(240000, "PlaceTreasures", {}); //Place more treasures after 4 minutes +} + +Trigger.prototype.InitializeEnemyWaves = function() +{ + var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); + cmpGUIInterface.PushNotification({ + "players": [1,2,3,4,5,6,7,8], + "message": markForTranslation("The first wave will start in 15 minutes!"), + "translateMessage": true + }); + cmpTrigger.DoAfterDelay(900000, "StartAnEnemyWave", {}); +} + +Trigger.prototype.DefeatPlayerOnceCCIsDestroyed = function(data) +{ + // Defeat a player that has lost his civic center + if ( data.entity == cmpTrigger.playerCivicCenter[data.from] && data.to == -1) + TriggerHelper.DefeatPlayer(data.from); + + // Check if only one player remains. He will be the winner. + var lastPlayerStanding = 0; + var numPlayersStanding = 0; + for (var i = 1; i < numberOfPlayers; ++i) + { + if (TriggerHelper.GetPlayerComponent(i).GetState() == "active") + { + lastPlayerStanding = i; + ++numPlayersStanding; + } + } + if (numPlayersStanding == 1) + TriggerHelper.SetPlayerWon(lastPlayerStanding); +} + +var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); +cmpTrigger.playerCivicCenter = {}; +cmpTrigger.DoAfterDelay(0, "InitGame", {}); +cmpTrigger.DoAfterDelay(1000, "InitializeEnemyWaves", {}); + +cmpTrigger.RegisterTrigger("OnOwnershipChanged", "DefeatPlayerOnceCCIsDestroyed", {"enabled": true}); \ No newline at end of file diff --git a/binaries/data/mods/public/maps/scripts/TriggerHelper.js b/binaries/data/mods/public/maps/scripts/TriggerHelper.js index 21b4d8a92d..e76c47b528 100644 --- a/binaries/data/mods/public/maps/scripts/TriggerHelper.js +++ b/binaries/data/mods/public/maps/scripts/TriggerHelper.js @@ -125,8 +125,42 @@ TriggerHelper.SetPlayerWon = function(playerID) */ TriggerHelper.DefeatPlayer = function(playerID) { - var playerEnt = GetPlayerEntityByID(playerID); + var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + var playerEnt = cmpPlayerMan.GetPlayerByID(playerID); Engine.PostMessage(playerEnt, MT_PlayerDefeated, { "playerId": playerID } ); }; +/** + * Returns the number of current players + */ +TriggerHelper.GetNumberOfPlayers = function() +{ + var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + return cmpPlayerManager.GetNumPlayers(); +} + +/** + * Returns the player component. For more information on its functions, see simulation/components/Player.js + */ +TriggerHelper.GetPlayerComponent = function(playerID) +{ + var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + return Engine.QueryInterface(cmpPlayerMan.GetPlayerByID(playerID), IID_Player); +} + +/** + * A function to determine if an entity has a specific class + * @param entity ID of the entity that we want to check for classes + * @param classname The name of the class we are checking if the entity has + */ +TriggerHelper.EntityHasClass = function(entity, classname) +{ + var cmpIdentity = Engine.QueryInterface(entity, IID_Identity); + if (!cmpIdentity) + return false; + var classes = cmpIdentity.GetClassesList();; + return (classes && classes.indexOf(classname) != -1); +} + + Engine.RegisterGlobal("TriggerHelper", TriggerHelper);