Skip to content

Commit

Permalink
Support the environment settings in games
Browse files Browse the repository at this point in the history
  • Loading branch information
RussellLVP committed Jul 6, 2020
1 parent 78bdae8 commit cc11ec0
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 8 deletions.
2 changes: 1 addition & 1 deletion javascript/features/games/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ derby and racing games.

## Options when registering a game
When registering a game with the `Games.registerGame()` function, you pass in a class that extends
the [Game class](game.js) and an options dictionary.
the [GameBase class](game_base.js) ([interface](game.js)) and an options dictionary.

### Required configuration

Expand Down
36 changes: 33 additions & 3 deletions javascript/features/games/environment_settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,43 @@
import { GameCustomSetting } from 'features/games/game_custom_setting.js';
import { Menu } from 'components/menu/menu.js';

// Configuration and mappings for the gravity options available to games.
const kGravityConfiguration = new Map([
[ 'Low', 0.005 ],
[ 'Normal', null ],
[ 'High', 0.011 ],
]);

// Configuration and mappings for the time options available to games.
const kTimeConfiguration = new Map([
[ 'Morning', 8 ],
[ 'Afternoon', 15 ],
[ 'Evening', 20 ],
[ 'Night', 2 ],
]);

// Configuration and mappings for the weather options available to games.
const kWeatherConfiguration = new Map([
[ 'Cloudy', 7 ],
[ 'Foggy', 9 ],
[ 'Heatwave', 11 ],
[ 'Rainy', 8 ],
[ 'Sandstorm', 19 ],
[ 'Sunny', 10 ],
]);

// Represents the environment for minigames, which can be customised to the player's liking. This
// includes the time, the weather, as well as the gravity level to apply.
export class EnvironmentSettings extends GameCustomSetting {
// Options available for each of the configuration values.
static kTimeOptions = [ 'Morning', 'Afternoon', 'Evening', 'Night' ];
static kWeatherOptions = [ 'Cloudy', 'Foggy', 'Heatwave', 'Rainy', 'Sandstorm', 'Sunny' ];
static kGravityOptions = [ 'Low', 'Normal', 'High' ];
static kGravityOptions = [ ...kGravityConfiguration.keys() ];
static kTimeOptions = [ ...kTimeConfiguration.keys() ];
static kWeatherOptions = [ ...kWeatherConfiguration.keys() ];

// Returns the actual in-game values for the given |option|.
static getGravityForOption(option) { return kGravityConfiguration.get(option) ?? null; }
static getTimeForOption(option) { return kTimeConfiguration.get(option) ?? null; }
static getWeatherForOption(option) { return kWeatherConfiguration.get(option) ?? null; }

// Returns the value that is to be displayed in the generic customization dialog for games.
getCustomizationDialogValue(currentValue) {
Expand Down
42 changes: 42 additions & 0 deletions javascript/features/games/game_base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2020 Las Venturas Playground. All rights reserved.
// Use of this source code is governed by the MIT license, a copy of which can
// be found in the LICENSE file.

import { EnvironmentSettings } from 'features/games/environment_settings.js';
import { Game } from 'features/games/game.js';

// Implementation of the Game class that provides the low-level functionality offered directly by
// the Games API. Most features will want to extend this class rather than Game directly.
export class GameBase extends Game {
#gravity_ = null;
#time_ = null;
#weather_ = null;

async onInitialized(settings, userData) {
const environment = settings.get('game/environment');
if (environment) {
this.#gravity_ = EnvironmentSettings.getGravityForOption(environment.gravity);
this.#time_ = EnvironmentSettings.getTimeForOption(environment.time);
this.#weather_ = EnvironmentSettings.getWeatherForOption(environment.weather);
}
}

async onPlayerAdded(player) {
// Update the gravity, time and weather for the |player|, but only when necessary.
if (this.#gravity_)
player.gravity = this.#gravity_;

if (this.#time_)
player.time = [ this.#time_, 20 ];

if (this.#weather_)
player.weather = this.#weather_;
}

async onPlayerRemoved(player) {
// Restore the gravity for the player. The |player|'s weather and time will be reset when
// their state gets deserialized immediately after this.
if (this.#gravity_)
player.gravity = Player.kDefaultGravity;
}
}
29 changes: 27 additions & 2 deletions javascript/features/games/game_runtime.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// Use of this source code is governed by the MIT license, a copy of which can
// be found in the LICENSE file.

import { EnvironmentSettings } from 'features/games/environment_settings.js';
import { GameActivity } from 'features/games/game_activity.js';
import { GameBase } from 'features/games/game_base.js';
import { GameDescription, kDefaultTickIntervalMs } from 'features/games/game_description.js';
import { GameRuntime } from 'features/games/game_runtime.js';
import { Game } from 'features/games/game.js';
Expand Down Expand Up @@ -247,14 +249,22 @@ describe('GameRuntime', (it, beforeEach) => {
minimumPlayers: 2,
maximumPlayers: 4,
price: 250,

environment: {
gravity: 'Low',
time: 'Afternoon',
weather: 'Foggy',
},
};

let vehicles = [ null, null ];
feature.registerGame(class extends Game {
feature.registerGame(class extends GameBase {
nextPlayerIndex_ = 0;
playerIndex_ = new Map();

async onInitialized(settings) {
async onInitialized(settings, userData) {
await super.onInitialized(settings, userData);

vehicles[0] = this.scopedEntities.createVehicle({
modelId: 432, // tank
position: new Vector(0, 0, 0),
Expand All @@ -267,14 +277,20 @@ describe('GameRuntime', (it, beforeEach) => {
}

async onPlayerAdded(player) {
await super.onPlayerAdded(player);

this.playerIndex_.set(player, this.nextPlayerIndex_++);
}

async onPlayerSpawned(player) {
await super.onPlayerSpawned(player);

player.enterVehicle(vehicles[this.playerIndex_.get(player)]);
}

async onPlayerDeath(player, killer, reason) {
await super.onPlayerDeath(player, killer, reason);

await this.playerLost(player);
await this.playerWon(killer, 25);
}
Expand All @@ -298,6 +314,9 @@ describe('GameRuntime', (it, beforeEach) => {
assert.equal(
manager.getPlayerActivity(russell).getActivityState(), GameActivity.kStateRegistered);

assert.deepEqual(gunther.time, [ 0, 0 ]);
assert.equal(gunther.gravity, Player.kDefaultGravity);

await server.clock.advance(settings.getValue('games/registration_expiration_sec') * 1000);
await runGameLoop();

Expand All @@ -314,6 +333,9 @@ describe('GameRuntime', (it, beforeEach) => {
assert.equal(
manager.getPlayerActivity(russell).getActivityState(), GameActivity.kStateEngaged);

assert.deepEqual(gunther.time, [ EnvironmentSettings.getTimeForOption('Afternoon'), 20 ]);
assert.equal(gunther.gravity, EnvironmentSettings.getGravityForOption('Low'));

assert.equal(runtime.state, GameRuntime.kStateRunning);

assert.isNotNull(vehicles[0]);
Expand Down Expand Up @@ -355,6 +377,9 @@ describe('GameRuntime', (it, beforeEach) => {
// Make sure that the money has been divided as expected.
assert.equal(finance.getPlayerCash(gunther), 150);
assert.equal(finance.getPlayerCash(russell), 1250);

// Make sure that gravity was reset.
assert.equal(gunther.gravity, Player.kDefaultGravity);
});

it('is able to proportionally calculate the prize money', assert => {
Expand Down
4 changes: 2 additions & 2 deletions javascript/features/games_deathmatch/deathmatch_game.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

import { Color } from 'base/color.js';
import { DeathmatchPlayerState } from 'features/games_deathmatch/deathmatch_player_state.js';
import { Game } from 'features/games/game.js';
import { GameBase } from 'features/games/game_base.js';

// Colours that will be assigned to participants of certain teams.
const kTeamColorAlpha = Color.fromHex('D84315AA'); // red
const kTeamColorBravo = Color.fromHex('0277BDAA'); // blue

// Implementation of the `Game` interface which extends it with deathmatch-related functionality. It
// exposes methods that should be called before game-specific behaviour, i.e. through super calls.
export class DeathmatchGame extends Game {
export class DeathmatchGame extends GameBase {
// Values for map marker visibility for the participants.
static kMapMarkersEnabled = 'Enabled';
static kMapMarkersEnabledTeam = 'Team only';
Expand Down

0 comments on commit cc11ec0

Please sign in to comment.