Skip to content
Tony Sparks edited this page Jun 26, 2018 · 4 revisions

Scripting

The Seventh uses Leola as its scripting engine. Maps may be scripted by creating the following files:

  • [mapname.json].client.props.leola
    • This is client side scripting to add visual/sound effects. This is non-authoritative of the server. This is always applied no matter the game type.
  • [mapname.json].client.[game type].leola
    • Same thing as the client.props.leola only that it is applied per game type.
  • [mapname.json].props.leola
    • This is server side scripting which allows the scripter to add game timers, triggers, spawn entities and listen for server side events. This is always applied no matter the game type.
  • [mapname.json].[game type].leola
    • Same thing as props.leola only that it is applied per game type.
  • [mapname.json].objects.leola
    • Any MapObjects defined in the map

Game Types

  • Team Death Match (tdm)
  • Objective (obj)
  • Capture the Flag (ctf)
  • Survivor (svr)

Game Events

  • BombDisarmedEvent
  • BombExplodedEvent
  • BombPlantedEvent
  • FlagCapturedEvent
  • FlagReturnedEvent
  • FlagStolenEvent
  • GameEndEvent
  • KillRollEvent
  • KillStreakEvent
  • PlayerAwardEvent
  • PlayerJoinedEvent
  • PlayerKilledEvent
  • PlayerLeftEvent
  • PlayerSpawnedEvent
  • RoundEndedEvent
  • RoundStartedEvent
  • SoundEmittedEvent
  • SurvivorEvent
  • TileRemovedEvent

Map Objects

A MapObject is something interactive on the map. They are different from an Entity because the state of the MapObject is not synchronized with the clients. The clients get the same MapObject as the server by loading the map; it assumes the server and clients map files are the same (which works if indeed the versions of the Map are identical -- in which cases where they do not match, the server is authoritative and the clients/users will have weird behavior).

When defining a MapObject, there are two parts:

  • The [mapname.json].objects.leola file defines all possible MapObjects. As an example file:
{ 
	"objects" : [
                // This defines a Car MapObject, players/bullets can't move thru it
		{
			"type": "CarA",        // defines the 'type' of MapObject -- this is important to match the type with TileD MapObject type
			"width" : 80,          // the width of this object
			"height" : 140,        // the height of this object
			"surfaceType" : "METAL",  // the surface type; used for if this MapObject is collidable, what sounds to make when bullets hit it
			"isCollidable" : true,    // if true, bullets/players collide with the MapObject
                        // [optional] Image to display on the client
			"image" : {    
				"path" : "./assets/gfx/map_objects/car_a.png",   // the image path
                                // the below are optional fields if you want to take a sub-image; if omitted, then the dimensions of the image file are used
				"x" : 4,         // the image x offset
				"y" : 9,         // the image y offset
				"width" : 145,   // the image width
				"height" : 311	 // the image height			
			}
		},
                // This defines a breakable window MapObject
		{
			"type": "Window",
			"width": 32,
			"height": 16,
			"surfaceType": "GLASS",
			"isCollidable": true,
			"onTouched": "breakWindow"  // the 'onTouched' is a leola function that is called when this MapObject is touched by an Entity.  The function may be defined in either `core.leola` or the `[mapname.json].props.leola` files
		},
		{
			"type": "ExplosiveCrate",
			"width": 32,
			"height": 32,
			"surfaceType": "WOOD",
			"isCollidable": true,
			"onTouched": "explodeCrate"
		}
}
  • The [mapname.json] itself is a TileD output format which includes MapObjects.

Here is TileD with the Window MapObject placed. The MapObject in TileD is matched by the type field defined in the Properties panel and the type field defined in the [mapname.json].objects.leola file.

Example Scripts

Server side script that always runs for the map Carentan

File name: carentan.json.props.leola

// spawn a tank
var tank = game.newTank(620, 835)

// Add Doors
var door1 = game.newDoor(607,940, -1, 0)
var door2 = game.newDoor(31,716, -1, 0)

// Add a light source
var light = game.newLight(647,960)
light.setLuminacity(1)
light.setColor(1,1,1)
light.setSize(128)


// Flicker a Light, randomly trigger the function between 50msec and 60sec looping
game.addRandomGameTimer(true, 50, 60, def() {
        // change the luminacity of the light source
	var lum = light.getLuminacity()
	if lum < 1 {
		light.setLuminacity(1)				
	}
	else {
		light.setLuminacity(0.25)				
	}
})


// When a door opens, cause an explosion
class DoorBomb(door) {
	var isActive = false
	
        // the game engine checks this trigger condition; if true will call the  execute method
        // after a trigger has been fired, it is removed
	var checkCondition = def(game) {
		return !isActive && door.isOpened()
	}
	
        // executes when the trigger condition is met
	var execute = def(game) {
		isActive = true
                // spawns an explosion, probably killing whoever opened the door
		game.newBigExplosion(door.getCenterPos(), door, 20, 15, 5)
		
		// add this trigger back after 5 seconds
		game.addGameTimer(false, 5000, def() {
			isActive = false
			game.addTrigger(this)
		})
	}
}

// add the door bomb trigger 
game.addTrigger(new DoorBomb(door1))


// When a player dies, spawn a health pack
game.addEventListener("PlayerKilledEvent", def(event) {
	console.println(event.getPlayer().getName() + " died by " + event.getMeansOfDeath().name())
	
	game.newHealthPack( event.getPlayer().getKilledAt() )
})

Client Side script that only runs if the game type is Team Death Match

File name: carentan.json.client.tdm.leola

// make this night time by adjusting the ambient light intensity, give it a blue hue too
var lightSystem = game.getLightSystem()
lightSystem.setAmbientColor(0.85, 0.85, 1)
lightSystem.setAmbientIntensity(0.7)

// load some ambient sounds (background explosions and crickets)
var ambientSounds = {			  
	bombs -> [game.loadSound("./assets/sfx/ambient/explo_distant01.wav"),
			  game.loadSound("./assets/sfx/ambient/explo_distant02.wav"),
			  game.loadSound("./assets/sfx/ambient/explo_distant03.wav"),
			  game.loadSound("./assets/sfx/ambient/explo_distant04.wav"),
			  game.loadSound("./assets/sfx/ambient/explo_distant05.wav"),
			  game.loadSound("./assets/sfx/ambient/explo_distant06.wav"),
			  game.loadSound("./assets/sfx/ambient/explo_distant07.wav"),
			  game.loadSound("./assets/sfx/ambient/explo_distant08.wav")],
			  
	cricket -> [game.loadSound("./assets/sfx/ambient/cricket.wav")],
}

// play a random sound of a sound type
var playSnd = def(type) {
	return def() {		
		var rand = game.getRandom()
		var index = rand.nextInt(length(ambientSounds[type]))

		var sound = ambientSounds[type][index]		
		if sound {		
			game.playGlobalSound(sound)
		}
	}
}

// when the Round Starts, game in some client side game timers that randomly
// play cricket and bomb sounds
var onRoundStarted = def(game) {
		
	game.addRandomGameTimer(true, 1000, 2500, playSnd("cricket"))		
	game.addRandomGameTimer(true, 5000, 12500, playSnd("bombs"))
}

// free the sound's from memory once the game is finished
var onDestroy = def(game) {
	ambientSounds.bombs.foreach(game.unloadSound)
	ambientSounds.cricket.foreach(game.unloadSound)
}
Clone this wiki locally