Files @ r25958:603d75b53498
Branch filter:

Location: cpp/openttd-patchpack/source/src/script/api/script_event_types.cpp

Patric Stout
Doc: update multiplayer documentation with latest changes (#9552)

Although several places were fixed during the PR making the change,
not all made it in this document.

While at it, removed all kinds of Markdown warnings by an excessive
usage of spacebar in this document.
/*
 * This file is part of OpenTTD.
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 */

/** @file script_event_types.cpp Implementation of all EventTypes. */

#include "../../stdafx.h"
#include "script_event_types.hpp"
#include "script_vehicle.hpp"
#include "script_log.hpp"
#include "../../strings_func.h"
#include "../../settings_type.h"
#include "../../engine_base.h"
#include "../../articulated_vehicles.h"
#include "../../string_func.h"
#include "table/strings.h"

#include "../../safeguards.h"

bool ScriptEventEnginePreview::IsEngineValid() const
{
	const Engine *e = ::Engine::GetIfValid(this->engine);
	return e != nullptr && e->IsEnabled();
}

char *ScriptEventEnginePreview::GetName()
{
	if (!this->IsEngineValid()) return nullptr;

	::SetDParam(0, this->engine);
	return GetString(STR_ENGINE_NAME);
}

CargoID ScriptEventEnginePreview::GetCargoType()
{
	if (!this->IsEngineValid()) return CT_INVALID;
	CargoArray cap = ::GetCapacityOfArticulatedParts(this->engine);

	CargoID most_cargo = CT_INVALID;
	uint amount = 0;
	for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
		if (cap[cid] > amount) {
			amount = cap[cid];
			most_cargo = cid;
		}
	}

	return most_cargo;
}

int32 ScriptEventEnginePreview::GetCapacity()
{
	if (!this->IsEngineValid()) return -1;
	const Engine *e = ::Engine::Get(this->engine);
	switch (e->type) {
		case VEH_ROAD:
		case VEH_TRAIN: {
			CargoArray capacities = GetCapacityOfArticulatedParts(this->engine);
			for (CargoID c = 0; c < NUM_CARGO; c++) {
				if (capacities[c] == 0) continue;
				return capacities[c];
			}
			return -1;
		}

		case VEH_SHIP:
		case VEH_AIRCRAFT:
			return e->GetDisplayDefaultCapacity();

		default: NOT_REACHED();
	}
}

int32 ScriptEventEnginePreview::GetMaxSpeed()
{
	if (!this->IsEngineValid()) return -1;
	const Engine *e = ::Engine::Get(this->engine);
	int32 max_speed = e->GetDisplayMaxSpeed(); // km-ish/h
	if (e->type == VEH_AIRCRAFT) max_speed /= _settings_game.vehicle.plane_speed;
	return max_speed;
}

Money ScriptEventEnginePreview::GetPrice()
{
	if (!this->IsEngineValid()) return -1;
	return ::Engine::Get(this->engine)->GetCost();
}

Money ScriptEventEnginePreview::GetRunningCost()
{
	if (!this->IsEngineValid()) return -1;
	return ::Engine::Get(this->engine)->GetRunningCost();
}

int32 ScriptEventEnginePreview::GetVehicleType()
{
	if (!this->IsEngineValid()) return ScriptVehicle::VT_INVALID;
	switch (::Engine::Get(this->engine)->type) {
		case VEH_ROAD:     return ScriptVehicle::VT_ROAD;
		case VEH_TRAIN:    return ScriptVehicle::VT_RAIL;
		case VEH_SHIP:     return ScriptVehicle::VT_WATER;
		case VEH_AIRCRAFT: return ScriptVehicle::VT_AIR;
		default: NOT_REACHED();
	}
}

bool ScriptEventEnginePreview::AcceptPreview()
{
	if (!this->IsEngineValid()) return false;
	return ScriptObject::DoCommand(0, this->engine, 0, CMD_WANT_ENGINE_PREVIEW);
}

bool ScriptEventCompanyAskMerger::AcceptMerger()
{
	return ScriptObject::DoCommand(0, this->owner, 0, CMD_BUY_COMPANY);
}

ScriptEventAdminPort::ScriptEventAdminPort(const std::string &json) :
		ScriptEvent(ET_ADMIN_PORT),
		json(json)
{
}

#define SKIP_EMPTY(p) while (*(p) == ' ' || *(p) == '\n' || *(p) == '\r') (p)++;
#define RETURN_ERROR(stack) { ScriptLog::Error("Received invalid JSON data from AdminPort."); if (stack != 0) sq_pop(vm, stack); return nullptr; }

SQInteger ScriptEventAdminPort::GetObject(HSQUIRRELVM vm)
{
	const char *p = this->json.c_str();

	if (this->ReadTable(vm, p) == nullptr) {
		sq_pushnull(vm);
		return 1;
	}

	return 1;
}

const char *ScriptEventAdminPort::ReadString(HSQUIRRELVM vm, const char *p)
{
	const char *value = p;

	bool escape = false;
	for (;;) {
		if (*p == '\\') {
			escape = true;
			p++;
			continue;
		}
		if (*p == '"' && escape) {
			escape = false;
			p++;
			continue;
		}
		escape = false;

		if (*p == '"') break;
		if (*p == '\0') RETURN_ERROR(0);

		p++;
	}

	size_t len = p - value;
	sq_pushstring(vm, value, len);
	p++; // Step past the end-of-string marker (")

	return p;
}

const char *ScriptEventAdminPort::ReadTable(HSQUIRRELVM vm, const char *p)
{
	sq_newtable(vm);

	SKIP_EMPTY(p);
	if (*p++ != '{') RETURN_ERROR(1);

	for (;;) {
		SKIP_EMPTY(p);
		if (*p++ != '"') RETURN_ERROR(1);

		p = ReadString(vm, p);
		if (p == nullptr) {
			sq_pop(vm, 1);
			return nullptr;
		}

		SKIP_EMPTY(p);
		if (*p++ != ':') RETURN_ERROR(2);

		p = this->ReadValue(vm, p);
		if (p == nullptr) {
			sq_pop(vm, 2);
			return nullptr;
		}

		sq_rawset(vm, -3);
		/* The key (-2) and value (-1) are popped from the stack by squirrel. */

		SKIP_EMPTY(p);
		if (*p == ',') {
			p++;
			continue;
		}
		break;
	}

	SKIP_EMPTY(p);
	if (*p++ != '}') RETURN_ERROR(1);

	return p;
}

const char *ScriptEventAdminPort::ReadValue(HSQUIRRELVM vm, const char *p)
{
	SKIP_EMPTY(p);

	if (strncmp(p, "false", 5) == 0) {
		sq_pushinteger(vm, 0);
		return p + 5;
	}
	if (strncmp(p, "true", 4) == 0) {
		sq_pushinteger(vm, 1);
		return p + 4;
	}
	if (strncmp(p, "null", 4) == 0) {
		sq_pushnull(vm);
		return p + 4;
	}

	switch (*p) {
		case '"': {
			/* String */
			p = ReadString(vm, ++p);
			if (p == nullptr) return nullptr;

			break;
		}

		case '{': {
			/* Table */
			p = this->ReadTable(vm, p);
			if (p == nullptr) return nullptr;

			break;
		}

		case '[': {
			/* Array */
			sq_newarray(vm, 0);

			/* Empty array? */
			const char *p2 = p + 1;
			SKIP_EMPTY(p2);
			if (*p2 == ']') {
				p = p2 + 1;
				break;
			}

			while (*p++ != ']') {
				p = this->ReadValue(vm, p);
				if (p == nullptr) {
					sq_pop(vm, 1);
					return nullptr;
				}
				sq_arrayappend(vm, -2);

				SKIP_EMPTY(p);
				if (*p == ',') continue;
				if (*p == ']') break;
				RETURN_ERROR(1);
			}

			p++;

			break;
		}

		case '1': case '2': case '3': case '4': case '5':
		case '6': case '7': case '8': case '9': case '0':
		case '-': {
			/* Integer */

			const char *value = p++;
			for (;;) {
				switch (*p++) {
					case '1': case '2': case '3': case '4': case '5':
					case '6': case '7': case '8': case '9': case '0':
						continue;

					default:
						break;
				}

				p--;
				break;
			}

			int res = atoi(value);
			sq_pushinteger(vm, (SQInteger)res);

			break;
		}

		default:
			RETURN_ERROR(0);
	}

	return p;
}

#undef SKIP_EMPTY
#undef RETURN_ERROR