Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,766 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/rect.h"
#include "common/util.h"
#include "scumm/he/intern_he.h"
#include "scumm/he/moonbase/moonbase.h"
#include "scumm/he/moonbase/ai_defenseunit.h"
#include "scumm/he/moonbase/ai_main.h"
namespace Scumm {
DefenseUnit::DefenseUnit(AI *ai) : _ai(ai) {
_state = DUS_ON;
_id = -1;
_distanceTo = 0;
_state = 0;
_radius = 0;
_armor = 0;
_cost = 0;
}
DefenseUnit::DefenseUnit(DefenseUnit *inUnit, AI *ai) : _ai(ai) {
_id = inUnit->getID();
_pos.x = inUnit->getPosX();
_pos.y = inUnit->getPosY();
_distanceTo = inUnit->getDistanceTo();
_state = inUnit->getState();
_radius = inUnit->getRadius();
_armor = inUnit->getArmor();
_cost = inUnit->getCost();
}
DefenseUnit::~DefenseUnit() {
}
Common::Point *AntiAirUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) {
float ratio;
int radius;
Common::Point *targetPos = new Common::Point;
if (!distance) distance = 1;
switch (weaponType) {
case ITEM_BOMB:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CLUSTER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CRAWLER:
radius = getRadius();
if ((distance < radius) || (getState() == DUS_OFF)) {
targetPos->x = getPosX();
targetPos->y = getPosY();
} else {
ratio = MAX(0, (getRadius() / distance));
targetPos->x = (int16)(getPosX() - ratio * (getPosX() - sourceX));
targetPos->y = (int16)(getPosY() - ratio * (getPosY() - sourceY));
}
break;
case ITEM_EMP:
if (getRadius() + 215 > distance) { // Emp radius
double x1 = static_cast<double>(sourceX);
double y1 = static_cast<double>(sourceY);
double x2 = static_cast<double>(getPosX());
double y2 = static_cast<double>(getPosY());
double r1 = static_cast<double>(215);
double r2 = static_cast<double>(getRadius() + 3);
double d = static_cast<double>(distance);
// Formulae for calculating one point of intersection of two circles
float root = sqrt((((r1 + r2) * (r1 + r2)) - (d * d)) * ((d * d) - ((r2 - r1) * (r2 - r1))));
int x = (int)(((x1 + x2) / 2) + ((x2 - x1) * (r1 * r1 - r2 * r2)) / (2 * d * d) + ((y2 - y1) / (2 * d * d)) * root);
int y = (int)(((y1 + y2) / 2) + ((y2 - y1) * (r1 * r1 - r2 * r2)) / (2 * d * d) - ((x2 - x1) / (2 * d * d)) * root);
targetPos->x = x;
targetPos->y = y;
} else {
ratio = 1 - (getRadius() / static_cast<float>(distance - 20));
targetPos->x = (int16)(sourceX + ratio * (getPosX() - sourceX));
targetPos->y = (int16)(sourceY + ratio * (getPosY() - sourceY));
}
break;
default:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
}
return targetPos;
}
int AntiAirUnit::selectWeapon(int index) {
switch (index) {
case 0:
return ITEM_CLUSTER;
break;
case 1:
return ITEM_EMP;
break;
case 2:
if (getState() == DUS_OFF) {
if (_ai->getPlayerEnergy() > 6) {
if (!_ai->_vm->_rnd.getRandomNumber(3)) {
return ITEM_VIRUS;
}
}
if (_ai->getPlayerEnergy() > 2) {
if (!_ai->_vm->_rnd.getRandomNumber(1)) {
return ITEM_SPIKE;
}
}
return ITEM_BOMB;
}
return ITEM_CLUSTER;
break;
default:
return ITEM_CLUSTER;
break;
}
}
Common::Point *ShieldUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) {
float ratio;
Common::Point *targetPos = new Common::Point;
if (getState() == DUS_OFF) {
targetPos->x = getPosX();
targetPos->y = getPosY();
} else {
switch (weaponType) {
case ITEM_BOMB:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CLUSTER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CRAWLER:
ratio = MAX(0.0, 1.0 - (static_cast<float>(getRadius()) / static_cast<float>(distance - 20)));
{
int maxX = _ai->getMaxX();
int maxY = _ai->getMaxY();
int thisX = (static_cast<int>(sourceX + ratio * (getPosX() - sourceX)) + maxX) % maxX;
int thisY = (static_cast<int>(sourceY + ratio * (getPosY() - sourceY)) + maxY) % maxY;
targetPos->x = thisX;
targetPos->y = thisY;
}
break;
case ITEM_EMP:
if (getRadius() + 215 > distance) { // Emp radius
double x1 = static_cast<double>(sourceX);
double y1 = static_cast<double>(sourceY);
double x2 = static_cast<double>(getPosX());
double y2 = static_cast<double>(getPosY());
double r1 = static_cast<double>(215);
double r2 = static_cast<double>(getRadius() + 10);
double d = static_cast<double>(distance);
// Formulae for calculating one point of intersection of two circles
float root = sqrt((((r1 + r2) * (r1 + r2)) - (d * d)) * ((d * d) - ((r2 - r1) * (r2 - r1))));
int x = (int)(((x1 + x2) / 2) + ((x2 - x1) * (r1 * r1 - r2 * r2)) / (2 * d * d) + ((y2 - y1) / (2 * d * d)) * root);
int y = (int)(((y1 + y2) / 2) + ((y2 - y1) * (r1 * r1 - r2 * r2)) / (2 * d * d) - ((x2 - x1) / (2 * d * d)) * root);
targetPos->x = x;
targetPos->y = y;
} else {
ratio = 1 - (getRadius() / static_cast<float>(distance - 20));
targetPos->x = (int16)(sourceX + ratio * (getPosX() - sourceX));
targetPos->y = (int16)(sourceY + ratio * (getPosY() - sourceY));
}
if (distance < getRadius()) {
targetPos->x = getPosX();
targetPos->y = getPosY();
}
break;
default:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
}
}
return targetPos;
}
int ShieldUnit::selectWeapon(int index) {
debugC(DEBUG_MOONBASE_AI, "Shield weapon select");
int myUnit = _ai->getClosestUnit(getPosX(), getPosY(), _ai->getMaxX(), _ai->getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 0);
int dist = _ai->getDistance(getPosX(), getPosY(), _ai->getHubX(myUnit), _ai->getHubY(myUnit));
if ((dist < (getRadius() - 20)) && (dist > 90)) {
return ITEM_SPIKE;
}
switch (index) {
case 0:
if (getState() == DUS_OFF) {
if (_ai->getPlayerEnergy() < 3) {
return ITEM_BOMB;
} else {
return ITEM_SPIKE;
}
}
return ITEM_EMP;
break;
case 1:
if (dist < getRadius() + 150) {
return ITEM_EMP;
} else {
return ITEM_CRAWLER;
}
break;
default:
return ITEM_EMP;
break;
}
}
Common::Point *MineUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) {
float ratio;
Common::Point *targetPos = new Common::Point;
switch (weaponType) {
case ITEM_BOMB:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CLUSTER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_EMP:
ratio = 1 - (getRadius() / static_cast<float>(distance - 20));
targetPos->x = (int16)(sourceX + ratio * (getPosX() - sourceX));
targetPos->y = (int16)(sourceY + ratio * (getPosY() - sourceY));
break;
default:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
}
return targetPos;
}
int MineUnit::selectWeapon(int index) {
int myUnit = _ai->getClosestUnit(getPosX(), getPosY(), _ai->getMaxX(), _ai->getCurrentPlayer(), 1, 0, 0, 0);
int x = getPosX();
int y = getPosY();
int dist = _ai->getDistance(x, y, _ai->getHubX(myUnit), _ai->getHubY(myUnit));
if ((getState() == DUS_ON) && (dist < 110)) {
return ITEM_EMP;
} else {
return ITEM_BOMB;
}
}
Common::Point *HubUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) {
Common::Point *targetPos = new Common::Point;
if (!distance) distance = 1;
switch (weaponType) {
case ITEM_BOMB:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CLUSTER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CRAWLER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
default:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
}
return targetPos;
}
int HubUnit::selectWeapon(int index) {
debugC(DEBUG_MOONBASE_AI, "Hub weapon select");
int energy = _ai->getPlayerEnergy();
if (energy > 6) {
//possibly choose crawler
if (_ai->getBuildingWorth(getID()) > 21) {
return ITEM_CRAWLER;
}
}
//choose betw/ bomb and cluster
if (_ai->getBuildingArmor(getID()) < 1.5) {
return ITEM_CLUSTER;
}
if (energy > 2) {
if (!_ai->_vm->_rnd.getRandomNumber(3)) {
return ITEM_SPIKE;
}
if (!_ai->_vm->_rnd.getRandomNumber(4)) {
return ITEM_GUIDED;
}
if (!_ai->_vm->_rnd.getRandomNumber(4)) {
return ITEM_MINE;
}
if (!_ai->_vm->_rnd.getRandomNumber(9)) {
return ITEM_EMP;
}
}
return ITEM_BOMB;
}
Common::Point *TowerUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) {
Common::Point *targetPos = new Common::Point;
if (!distance) distance = 1;
switch (weaponType) {
case ITEM_BOMB:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_SPIKE:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
default:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
}
return targetPos;
}
int TowerUnit::selectWeapon(int index) {
switch (index) {
case 0:
return ITEM_SPIKE;
break;
default:
return ITEM_SPIKE;
break;
}
}
Common::Point *BridgeUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) {
Common::Point *targetPos = new Common::Point;
if (!distance) distance = 1;
switch (weaponType) {
case ITEM_BOMB:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CLUSTER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
default:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
}
return targetPos;
}
int BridgeUnit::selectWeapon(int index) {
switch (index) {
case 0:
return ITEM_BOMB;
break;
case 1:
return ITEM_CLUSTER;
break;
default:
return ITEM_BOMB;
break;
}
}
Common::Point *EnergyUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) {
Common::Point *targetPos = new Common::Point;
if (!distance) distance = 1;
switch (weaponType) {
case ITEM_BOMB:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CLUSTER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CRAWLER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
default:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
}
return targetPos;
}
int EnergyUnit::selectWeapon(int index) {
debugC(DEBUG_MOONBASE_AI, "Energy weapon select");
int energy = _ai->getPlayerEnergy();
if (energy > 6) {
//possibly choose crawler
if (_ai->getBuildingWorth(getID()) > 21) {
return ITEM_CRAWLER;
}
}
//choose betw/ bomb and cluster
if (_ai->getBuildingArmor(getID()) < 1.5) {
return ITEM_CLUSTER;
}
if (energy > 2) {
if (!_ai->_vm->_rnd.getRandomNumber(3)) {
return ITEM_EMP;
}
}
return ITEM_BOMB;
}
Common::Point *OffenseUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) {
Common::Point *targetPos = new Common::Point;
if (!distance) distance = 1;
switch (weaponType) {
case ITEM_BOMB:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CLUSTER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CRAWLER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
default:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
}
return targetPos;
}
int OffenseUnit::selectWeapon(int index) {
debugC(DEBUG_MOONBASE_AI, "Offense weapon select");
int energy = _ai->getPlayerEnergy();
if (energy > 6) {
//possibly choose crawler
if (_ai->getBuildingWorth(getID()) > 21) {
return ITEM_CRAWLER;
}
}
//choose betw/ bomb and cluster
if (_ai->getBuildingArmor(getID()) < 1.5) {
return ITEM_CLUSTER;
}
return ITEM_BOMB;
}
Common::Point *CrawlerUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) {
Common::Point *targetPos = new Common::Point;
if (!distance)
distance = 1;
switch (weaponType) {
case ITEM_BOMB:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CLUSTER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
case ITEM_CRAWLER:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
default:
targetPos->x = getPosX();
targetPos->y = getPosY();
break;
}
return targetPos;
}
int CrawlerUnit::selectWeapon(int index) {
debugC(DEBUG_MOONBASE_AI, "Crawler weapon select");
int myUnit = _ai->getClosestUnit(getPosX(), getPosY(), _ai->getMaxX(), _ai->getCurrentPlayer(), 1, 0, 0, 0);
int dist = _ai->getDistance(_ai->getHubX(myUnit), _ai->getHubY(myUnit), getPosX(), getPosY());
int x = getPosX();
int y = getPosY();
int energy = _ai->getPlayerEnergy();
int terrain = _ai->getTerrain(x, y);
if (terrain != TERRAIN_TYPE_WATER) {
if ((energy > 2) && (dist < 220)) {
return ITEM_RECLAIMER;
} else {
return ITEM_BOMB;
}
} else {
if (energy > 6) {
return ITEM_CRAWLER;
}
if (energy > 2) {
if (_ai->_vm->_rnd.getRandomNumber(1)) {
return ITEM_MINE;
} else {
return ITEM_TIME_EXPIRED;
}
}
}
return SKIP_TURN;
}
AntiAirUnit::AntiAirUnit(AI *ai) : DefenseUnit(ai) {
setRadius(190);
setArmor(3);
setCost(1);
}
ShieldUnit::ShieldUnit(AI *ai) : DefenseUnit(ai) {
setRadius(170);
setArmor(3);
setCost(7);
}
MineUnit::MineUnit(AI *ai) : DefenseUnit(ai) {
setRadius(80);
setArmor(1);
setCost(3);
}
HubUnit::HubUnit(AI *ai) : DefenseUnit(ai) {
setRadius(1);
setArmor(5);
setCost(7);
}
TowerUnit::TowerUnit(AI *ai) : DefenseUnit(ai) {
setRadius(1);
setArmor(3);
setCost(1);
}
BridgeUnit::BridgeUnit(AI *ai) : DefenseUnit(ai) {
setRadius(1);
setArmor(3);
setCost(1);
}
EnergyUnit::EnergyUnit(AI *ai) : DefenseUnit(ai) {
setRadius(1);
setArmor(5);
setCost(7);
}
OffenseUnit::OffenseUnit(AI *ai) : DefenseUnit(ai) {
setRadius(1);
setArmor(3);
setCost(7);
}
CrawlerUnit::CrawlerUnit(AI *ai) : DefenseUnit(ai) {
setRadius(1);
setArmor(3);
setCost(7);
}
AntiAirUnit::AntiAirUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) {
setID(inUnit->getID());
setPos(inUnit->getPosX(), inUnit->getPosY());
setDistanceTo(inUnit->getDistanceTo());
setState(inUnit->getState());
setRadius(inUnit->getRadius());
setArmor(inUnit->getArmor());
}
ShieldUnit::ShieldUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) {
setID(inUnit->getID());
setPos(inUnit->getPosX(), inUnit->getPosY());
setDistanceTo(inUnit->getDistanceTo());
setState(inUnit->getState());
setRadius(inUnit->getRadius());
setArmor(inUnit->getArmor());
}
MineUnit::MineUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) {
setID(inUnit->getID());
setPos(inUnit->getPosX(), inUnit->getPosY());
setDistanceTo(inUnit->getDistanceTo());
setState(inUnit->getState());
setRadius(inUnit->getRadius());
setArmor(inUnit->getArmor());
}
HubUnit::HubUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) {
setID(inUnit->getID());
setPos(inUnit->getPosX(), inUnit->getPosY());
setDistanceTo(inUnit->getDistanceTo());
setState(inUnit->getState());
setRadius(inUnit->getRadius());
setArmor(inUnit->getArmor());
}
TowerUnit::TowerUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) {
setID(inUnit->getID());
setPos(inUnit->getPosX(), inUnit->getPosY());
setDistanceTo(inUnit->getDistanceTo());
setState(inUnit->getState());
setRadius(inUnit->getRadius());
setArmor(inUnit->getArmor());
}
BridgeUnit::BridgeUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) {
setID(inUnit->getID());
setPos(inUnit->getPosX(), inUnit->getPosY());
setDistanceTo(inUnit->getDistanceTo());
setState(inUnit->getState());
setRadius(inUnit->getRadius());
setArmor(inUnit->getArmor());
}
EnergyUnit::EnergyUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) {
setID(inUnit->getID());
setPos(inUnit->getPosX(), inUnit->getPosY());
setDistanceTo(inUnit->getDistanceTo());
setState(inUnit->getState());
setRadius(inUnit->getRadius());
setArmor(inUnit->getArmor());
}
OffenseUnit::OffenseUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) {
setID(inUnit->getID());
setPos(inUnit->getPosX(), inUnit->getPosY());
setDistanceTo(inUnit->getDistanceTo());
setState(inUnit->getState());
setRadius(inUnit->getRadius());
setArmor(inUnit->getArmor());
}
CrawlerUnit::CrawlerUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) {
setID(inUnit->getID());
setPos(inUnit->getPosX(), inUnit->getPosY());
setDistanceTo(inUnit->getDistanceTo());
setState(inUnit->getState());
setRadius(inUnit->getRadius());
setArmor(inUnit->getArmor());
}
} // End of namespace Scumm

View File

@@ -0,0 +1,194 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_AI_DEFENCEUNIT_H
#define SCUMM_HE_MOONBASE_AI_DEFENCEUNIT_H
namespace Scumm {
class AI;
enum {
DUT_ANTI_AIR = 1,
DUT_SHIELD = 2,
DUT_MINE = 3,
DUT_HUB = 4,
DUT_TOWER = 5,
DUT_BRIDGE = 6,
DUT_ENERGY = 7,
DUT_OFFENSE = 8,
DUT_CRAWLER = 9
};
enum {
DUS_ON = 1,
DUS_OFF = 2,
DUS_DESTROYED = 3
};
class DefenseUnit {
private:
int _id;
Common::Point _pos;
int _distanceTo;
int _state;
int _radius;
int _armor;
int _cost;
protected:
AI *_ai;
public:
DefenseUnit(AI *ai);
DefenseUnit(DefenseUnit *inUnit, AI *ai);
virtual ~DefenseUnit();
void setID(int id) { _id = id; }
void setDistanceTo(int distanceTo) { _distanceTo = distanceTo; }
void setState(int state) { _state = state; }
void setRadius(int radius) { _radius = radius; }
void setArmor(int armor) { _armor = armor; }
void setDamage(int damage) { _armor -= damage; }
void setPos(int x, int y) {
_pos.x = x;
_pos.y = y;
}
void setCost(int cost) { _cost = cost; }
int getID() const { return _id; }
int getDistanceTo() const { return _distanceTo; }
int getState() const { return _state; }
int getRadius() const { return _radius; }
int getArmor() const { return _armor; }
int getPosX() const { return _pos.x; }
int getPosY() const { return _pos.y; }
int getCost() const { return _cost; }
virtual int getType() const = 0;
virtual Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) = 0;
virtual int selectWeapon(int index) = 0;
};
class AntiAirUnit : public DefenseUnit {
private:
public:
AntiAirUnit(AI *ai);
AntiAirUnit(DefenseUnit *inUnit, AI *ai);
int getType() const override { return DUT_ANTI_AIR; }
Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) override;
int selectWeapon(int index) override;
};
class ShieldUnit : public DefenseUnit {
private:
public:
ShieldUnit(AI *ai);
ShieldUnit(DefenseUnit *inUnit, AI *ai);
int getType() const override { return DUT_SHIELD; }
Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) override;
int selectWeapon(int index) override;
};
class MineUnit : public DefenseUnit {
private:
public:
MineUnit(AI *ai);
MineUnit(DefenseUnit *inUnit, AI *ai);
int getType() const override { return DUT_MINE; }
Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) override;
int selectWeapon(int index) override;
};
class HubUnit : public DefenseUnit {
private:
public:
HubUnit(AI *ai);
HubUnit(DefenseUnit *inUnit, AI *ai);
int getType() const override { return DUT_HUB; }
Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) override;
int selectWeapon(int index) override;
};
class TowerUnit : public DefenseUnit {
private:
public:
TowerUnit(AI *ai);
TowerUnit(DefenseUnit *inUnit, AI *ai);
int getType() const override { return DUT_TOWER; }
Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) override;
int selectWeapon(int index) override;
};
class BridgeUnit : public DefenseUnit {
private:
public:
BridgeUnit(AI *ai);
BridgeUnit(DefenseUnit *inUnit, AI *ai);
int getType() const override { return DUT_BRIDGE; }
Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) override;
int selectWeapon(int index) override;
};
class EnergyUnit : public DefenseUnit {
private:
public:
EnergyUnit(AI *ai);
EnergyUnit(DefenseUnit *inUnit, AI *ai);
int getType() const override { return DUT_ENERGY; }
Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) override;
int selectWeapon(int index) override;
};
class OffenseUnit : public DefenseUnit {
private:
public:
OffenseUnit(AI *ai);
OffenseUnit(DefenseUnit *inUnit, AI *ai);
int getType() const override { return DUT_OFFENSE; }
Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) override;
int selectWeapon(int index) override;
};
class CrawlerUnit : public DefenseUnit {
private:
public:
CrawlerUnit(AI *ai);
CrawlerUnit(DefenseUnit *inUnit, AI *ai);
int getType() const override { return DUT_CRAWLER; }
Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) override;
int selectWeapon(int index) override;
};
} // End of namespace Scumm
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,210 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_AI_MAIN_H
#define SCUMM_HE_MOONBASE_AI_MAIN_H
#include "common/array.h"
#include "scumm/he/moonbase/ai_tree.h"
namespace Scumm {
class ScummEngine_v100he;
class AIEntity;
class patternList;
enum {
TERRAIN_TYPE_GOOD = 0,
TERRAIN_TYPE_SLOPE = 1,
TERRAIN_TYPE_WATER = 2,
MAX_MEMORY = 3
};
enum {
ITEM_BOMB = 0,
ITEM_CLUSTER = 1,
ITEM_REPAIR = 2,
ITEM_ANTIAIR = 3,
ITEM_BRIDGE = 4,
ITEM_TOWER = 5,
ITEM_GUIDED = 6,
ITEM_EMP = 7,
ITEM_SPIKE = 8,
ITEM_RECLAIMER = 9,
ITEM_BALLOON = 10,
ITEM_MINE = 11,
ITEM_CRAWLER = 12,
ITEM_VIRUS = 13,
ITEM_ENERGY = 14,
ITEM_SHIELD = 15,
ITEM_OFFENSE = 16,
ITEM_HUB = 17,
ITEM_TIME_EXPIRED = 18,
SKIP_TURN = -999
};
enum BuildingTypes {
BUILDING_ENERGY_COLLECTOR = 3,
BUILDING_MAIN_BASE = 4,
BUILDING_BRIDGE = 5,
BUILDING_TOWER = 6,
BUILDING_EXPLOSIVE_MINE = 7,
BUILDING_SHIELD = 8,
BUILDING_ANTI_AIR = 9,
BUILDING_OFFENSIVE_LAUNCHER = 10,
BUILDING_BALLOON = 11,
BUILDING_CRAWLER = 12
};
enum {
ENERGY_POOL_X = 45,
ENERGY_POOL_Y = 46,
ENERGY_POOL_UNITS_ON = 47,
MIN_DIST = 108
};
class AI {
public:
AI(ScummEngine_v100he *vm);
void resetAI();
void cleanUpAI();
void setAIType(const int paramCount, const int32 *params);
int masterControlProgram(const int paramCount, const int32 *params);
private:
int chooseBehavior();
int chooseTarget(int behavior);
Tree *initApproachTarget(int targetX, int targetY, Node **retNode);
int *approachTarget(Tree *myTree, int &x, int &y, Node **currentNode);
Tree *initAcquireTarget(int targetX, int targetY, Node **retNode);
int *acquireTarget(int targetX, int targetY);
int *acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode);
int *offendTarget(int &targetX, int &targetY, int index);
int *defendTarget(int &targetX, int &targetY, int index);
int *energizeTarget(int &targetX, int &targetY, int index);
public:
int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled);
int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled, int minDist);
int getDistance(int originX, int originY, int endX, int endY);
int calcAngle(int originX, int originY, int endX, int endY);
int calcAngle(int originX, int originY, int endX, int endY, int noWrapFlag);
int getTerrain(int x, int y);
int getHubX(int hub);
int getHubY(int hub);
int getMaxX();
int getMaxY();
int getCurrentPlayer();
int getMaxPower();
int getMinPower();
int getTerrainSquareSize();
int getBuildingOwner(int building);
int getBuildingState(int building);
int getBuildingType(int building);
int getBuildingArmor(int building);
int getBuildingMaxArmor(int building);
int getBuildingWorth(int building);
int getBuildingTeam(int building);
int getPlayerEnergy();
int getPlayerMaxTime();
int getTimerValue(int timerNum);
int getPlayerTeam(int player);
int getAnimSpeed();
int simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int isEnergy);
int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold, int olFlag);
int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold);
int checkIfWaterState(int x, int y);
int getUnitsWithinRadius(int x, int y, int radius);
float degToRad(float degrees);
int getEnergyHogType();
private:
int getEnergyPoolsArray();
int getCoordinateVisibility(int x, int y, int playerNum);
int getUnitVisibility(int unit, int playerNum);
int getEnergyPoolVisibility(int pool, int playerNum);
int getNumberOfPools();
int getNumberOfPlayers();
int getWindXSpeed();
int getWindYSpeed();
int getTotalWindSpeed();
int getWindXSpeedMax();
int getWindYSpeedMax();
int getBigXSize();
int getBigYSize();
int getEnergyPoolWidth(int pool);
int getLastAttacked(int &x, int &y);
int getFOW();
int getBuildingStackPtr();
int getTurnCounter();
int getGroundAltitude(int x, int y);
int checkForCordOverlap(int xStart, int yStart, int affectRadius, int simulateFlag);
int checkForAngleOverlap(int unit, int angle);
int estimateNextRoundEnergy(int player);
int checkForUnitOverlap(int x, int y, int radius, int ignoredUnit);
int checkForEnergySquare(int x, int y);
int aiChat();
int simulateWeaponLaunch(int x, int y, int power, int angle, int numSteps);
int fakeSimulateWeaponLaunch(int x, int y, int power, int angle);
int checkIfWaterSquare(int x, int y);
int getLandingPoint(int x, int y, int power, int angle);
int getEnemyUnitsVisible(int playerNum);
void limitLocation(int &a, int &b, int c, int d);
int energyPoolSize(int pool);
int getMaxCollectors(int pool);
public:
Common::Array<int> _lastXCoord[5];
Common::Array<int> _lastYCoord[5];
ScummEngine_v100he *_vm;
AIEntity *_aiType[5];
int _aiState;
int _behavior;
int _energyHogType;
patternList *_moveList[5];
const int32 *_mcpParams;
};
} // End of namespace Scumm
#endif

View File

@@ -0,0 +1,152 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "scumm/he/moonbase/ai_node.h"
namespace Scumm {
IContainedObject::IContainedObject(IContainedObject &sourceContainedObject) {
_objID = sourceContainedObject.getObjID();
_valueG = sourceContainedObject.getG();
}
int Node::_nodeCount = 0;
Node::Node() {
_parent = nullptr;
_depth = 0;
_nodeCount++;
_contents = nullptr;
}
Node::Node(Node *sourceNode) {
_parent = nullptr;
_children = sourceNode->getChildren();
_depth = sourceNode->getDepth();
_contents = sourceNode->getContainedObject()->duplicate();
}
Node::~Node() {
if (_contents != nullptr) {
delete _contents;
_contents = nullptr;
}
_nodeCount--;
}
int Node::generateChildren() {
int numChildren = _contents->numChildrenToGen();
int numChildrenGenerated = numChildren;
int errorCode = -1;
static int i = 0;
while (i < numChildren) {
Node *tempNode = new Node;
_children.push_back(tempNode);
tempNode->setParent(this);
tempNode->setDepth(_depth + 1);
int completionFlag;
IContainedObject *thisContObj = _contents->createChildObj(i, completionFlag);
assert(!(thisContObj != nullptr && completionFlag == 0));
if (!completionFlag) {
_children.pop_back();
delete tempNode;
return 0;
}
i++;
if (thisContObj != nullptr) {
tempNode->setContainedObject(thisContObj);
} else {
_children.pop_back();
delete tempNode;
numChildrenGenerated--;
}
}
i = 0;
if (numChildrenGenerated > 0)
return numChildrenGenerated;
return errorCode;
}
int Node::generateNextChild() {
int numChildren = _contents->numChildrenToGen();
static int i = 0;
Node *tempNode = new Node;
_children.push_back(tempNode);
tempNode->setParent(this);
tempNode->setDepth(_depth + 1);
int compFlag;
IContainedObject *thisContObj = _contents->createChildObj(i, compFlag);
if (thisContObj != nullptr) {
tempNode->setContainedObject(thisContObj);
} else {
_children.pop_back();
delete tempNode;
}
++i;
if (i > numChildren)
i = 0;
return i;
}
Node *Node::popChild() {
Node *temp;
temp = _children.back();
_children.pop_back();
return temp;
}
Node *Node::getFirstStep() {
Node *currentNode = this;
if (currentNode->getParent() == nullptr)
return currentNode;
while (currentNode->getParent()->getParent() != nullptr)
currentNode = currentNode->getParent();
assert(currentNode->getDepth() == 1);
return currentNode;
}
} // End of namespace Scumm

View File

@@ -0,0 +1,102 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_AI_NODE_H
#define SCUMM_HE_MOONBASE_AI_NODE_H
#include "common/array.h"
namespace Scumm {
const float SUCCESS = -1;
const float FAILURE = 1e20f;
class IContainedObject {
private:
int _objID;
float _valueG;
protected:
virtual float getG() const { return _valueG; }
virtual float calcH() { return 0; }
public:
IContainedObject() { _valueG = 0; _objID = -1; }
IContainedObject(float inG) { _valueG = inG; _objID = -1; }
IContainedObject(IContainedObject &sourceContainedObject);
virtual ~IContainedObject() {}
virtual IContainedObject *duplicate() = 0;
void setValueG(float inG) { _valueG = inG; }
float getValueG() { return _valueG; }
int getObjID() const { return _objID; }
void setObjID(int inputObjID) { _objID = inputObjID; }
virtual int numChildrenToGen() = 0;
virtual IContainedObject *createChildObj(int index, int &completionFlag) = 0;
virtual int checkSuccess() = 0;
virtual float calcT() { return getG(); }
float returnG() const { return getG(); }
};
class Node {
private:
Node *_parent;
Common::Array<Node *> _children;
int _depth;
static int _nodeCount;
IContainedObject *_contents;
public:
Node();
Node(Node *sourceNode);
~Node();
void setParent(Node *parentPtr) { _parent = parentPtr; }
Node *getParent() const { return _parent; }
void setDepth(int depth) { _depth = depth; }
int getDepth() const { return _depth; }
static int getNodeCount() { return _nodeCount; }
void setContainedObject(IContainedObject *value) { _contents = value; }
IContainedObject *getContainedObject() { return _contents; }
Common::Array<Node *> getChildren() const { return _children; }
int generateChildren();
int generateNextChild();
Node *popChild();
float getObjectT() { return _contents->calcT(); }
Node *getFirstStep();
};
} // End of namespace Scumm
#endif

View File

@@ -0,0 +1,161 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_AI_PATTERN_H
#define SCUMM_HE_MOONBASE_AI_PATTERN_H
namespace Scumm {
const int NO_PATTERN = 0;
const int PATTERN_FOUND = 1;
class patternInstance {
private:
int _sourceHub;
int _unit;
int _power;
int _angle;
public:
patternInstance() {
_sourceHub = 0;
_unit = 0;
_power = 0;
_angle = 0;
}
patternInstance(int sh, int unit, int power, int angle) {
setSourceHub(sh);
setUnit(unit);
setPower(power);
setAngle(angle);
}
void setSourceHub(int sh) { _sourceHub = sh; }
void setUnit(int unit) { _unit = unit; }
void setPower(int power) {
if (power < 300)
_power = 1;
else if (power < 480)
_power = 2;
else
_power = 3;
}
void setAngle(int angle) {
int tempAngle = angle % 360;
if ((tempAngle >= 0) && (tempAngle < 90))
_angle = 1;
if ((tempAngle >= 90) && (tempAngle < 180))
_angle = 2;
if ((tempAngle >= 180) && (tempAngle < 270))
_angle = 3;
if ((tempAngle >= 270))
_angle = 4;
}
int getSourceHub() const { return _sourceHub; }
int getUnit() const { return _unit; }
int getPowerIndex() const { return _power; }
int getAngleIndex() const { return _angle; }
static int comparePatterns(patternInstance *p1, patternInstance *p2) {
if (p1->getSourceHub() != p2->getSourceHub())
return 0;
if (p1->getUnit() != p2->getUnit())
return 0;
if (p1->getUnit() == -999)
return 0;
int temp = abs(p1->getPowerIndex() - p2->getPowerIndex());
if (temp > 1)
return 0;
temp = abs(p1->getAngleIndex() - p2->getAngleIndex());
if (temp > 1 && temp < 3)
return 0;
return 1;
}
};
class patternList {
private:
patternInstance *theList[10];
int listIndex;
public:
patternList() {
for (int i = 0; i < 10; i++) {
theList[i] = new patternInstance();
}
listIndex = 0;
}
~patternList() {
for (int i = 0; i < 10; i++) {
delete theList[i];
}
}
void addPattern(int sh, int unit, int power, int angle) {
theList[listIndex]->setSourceHub(sh);
theList[listIndex]->setUnit(unit);
theList[listIndex]->setPower(power);
theList[listIndex]->setAngle(angle);
listIndex++;
if (listIndex > 9)
listIndex = 0;
}
int evaluatePattern(int sh, int unit, int power, int angle) {
patternInstance *patternToMatch = new patternInstance(sh, unit, power, angle);
int matchCount = 0;
for (int i = 0; i < 9; i++) {
if (patternInstance::comparePatterns(theList[i], patternToMatch)) {
matchCount++;
}
}
delete patternToMatch;
if (matchCount > 2)
return PATTERN_FOUND;
return NO_PATTERN;
}
};
} // End of namespace Scumm
#endif

View File

@@ -0,0 +1,569 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "scumm/he/intern_he.h"
#include "scumm/he/moonbase/moonbase.h"
#include "scumm/he/moonbase/ai_targetacquisition.h"
#include "scumm/he/moonbase/ai_main.h"
#include "scumm/he/moonbase/ai_weapon.h"
namespace Scumm {
int Sortie::_sSourceX = 0;
int Sortie::_sSourceY = 0;
int Sortie::_sTargetX = 0;
int Sortie::_sTargetY = 0;
Sortie::~Sortie() {
for (Common::Array<DefenseUnit *>::iterator k = _enemyDefenses.begin(); k != _enemyDefenses.end(); k++) {
delete *k;
}
}
void Sortie::setEnemyDefenses(int enemyDefensesScummArray, int defendX, int defendY) {
DefenseUnit *thisUnit;
int currentPlayer = _ai->getCurrentPlayer();
for (int i = 0; i < 200; i++) {
int thisElement = _ai->_vm->_moonbase->readFromArray(enemyDefensesScummArray, 0, i);
if (thisElement) {
if (_ai->getBuildingOwner(thisElement)) {
if (_ai->getPlayerTeam(currentPlayer) != _ai->getBuildingTeam(thisElement)) {
int type = _ai->getBuildingType(thisElement);
switch (type) {
case BUILDING_ANTI_AIR:
thisUnit = new AntiAirUnit(_ai);
break;
case BUILDING_SHIELD:
thisUnit = new ShieldUnit(_ai);
break;
case BUILDING_EXPLOSIVE_MINE:
if (_ai->getDistance(_ai->getHubX(thisElement), _ai->getHubY(thisElement), defendX, defendY) < 90)
thisUnit = new MineUnit(_ai);
else
thisUnit = NULL;
break;
case BUILDING_CRAWLER:
thisUnit = NULL;
break;
default:
thisUnit = NULL;
break;
}
if (thisUnit != NULL) {
thisUnit->setID(thisElement);
thisUnit->setPos(_ai->getHubX(thisElement), _ai->getHubY(thisElement));
if (_ai->getBuildingState(thisElement)) thisUnit->setState(DUS_OFF);
_enemyDefenses.push_back(thisUnit);
}
}
}
} else {
i = 200;
}
}
}
int *Sortie::getShotPos() const {
int *retVal = new int[2];
retVal[0] = _shotPosX;
retVal[1] = _shotPosY;
return retVal;
}
int Sortie::numChildrenToGen() {
int retVal = MAX<uint>(_enemyDefenses.size(), 1) * NUM_SHOT_POSITIONS * NUM_WEAPONS;
return retVal;
}
IContainedObject *Sortie::createChildObj(int index, int &completionFlag) {
float thisDamage;
Sortie *retSortie = new Sortie(_ai);
// int activeDefenses = 0;
Common::Array<DefenseUnit *> thisEnemyDefenses;
// Copy the defensive unit list from the parent
for (Common::Array<DefenseUnit *>::iterator k = _enemyDefenses.begin(); k != _enemyDefenses.end(); k++) {
DefenseUnit *temp;
switch ((*k)->getType()) {
case DUT_ANTI_AIR:
temp = new AntiAirUnit(*k, _ai);
break;
case DUT_SHIELD:
temp = new ShieldUnit(*k, _ai);
break;
case DUT_MINE:
temp = new MineUnit(*k, _ai);
break;
case DUT_CRAWLER:
temp = new CrawlerUnit(*k, _ai);
break;
default:
temp = new ShieldUnit(*k, _ai);
break;
}
thisEnemyDefenses.push_back(temp);
}
// Calculate the current target from the index
DefenseUnit *currentTarget = *(thisEnemyDefenses.begin() + static_cast<int>(index / (NUM_WEAPONS * NUM_SHOT_POSITIONS)));
assert(currentTarget);
// Pick correct weapon according to index
Weapon *currentWeapon = new Weapon(currentTarget->selectWeapon(index % NUM_WEAPONS));
retSortie->setUnitType(currentWeapon->getTypeID());
// Calculate distance from target to source hub
int distance = _ai->getDistance(currentTarget->getPosX(), currentTarget->getPosY(), getSourcePosX(), getSourcePosY());
// Pick correct shot position according to index
Common::Point *targetCoords;
targetCoords = currentTarget->createTargetPos((static_cast<int>(index / NUM_WEAPONS) % NUM_SHOT_POSITIONS), distance, currentWeapon->getTypeID(), getSourcePosX(), getSourcePosY());
retSortie->setShotPos(targetCoords->x, targetCoords->y);
// Set the g value based on cost of the weapon
retSortie->setValueG(getG() + currentWeapon->getCost());
int AAcounter = 3;
// Loop through defensive units, toggling anti-air units and deciding if this weapon will land safely
for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) {
distance = _ai->getDistance((*i)->getPosX(), (*i)->getPosY(), targetCoords->x, targetCoords->y);
// Check to see if we're within an active defense's radius
if ((distance < (*i)->getRadius()) && ((*i)->getState() == DUS_ON)) {
//activeDefenses++;
// Turn off this anti-air and drop the coverage count
if (((*i)->getType() == DUT_ANTI_AIR)) {
(*i)->setState(DUS_OFF);
if (currentWeapon->getTypeID() == ITEM_CLUSTER)
AAcounter--;
else
AAcounter = 0;
}
// Essentially disable this weapon choice, due to its impact with a shield, or untriggered anti-air
if (((*i)->getType() == DUT_SHIELD) || !AAcounter) {
retSortie->setValueG(1000);
i = thisEnemyDefenses.end() - 1;
}
} else {
// Turn on any anti-airs that were off the previous turn
if (((*i)->getType() == DUT_ANTI_AIR) && ((*i)->getState() == DUS_OFF))
(*i)->setState(DUS_ON);
}
}
// Turn on all the non-anti-air units in preparation for emp's and the next turn
for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) {
if ((*i)->getType() != DUT_ANTI_AIR) {
(*i)->setState(DUS_ON);
}
}
// If this weapon is still valid
if (retSortie->getValueG() < 1000) {
// Apply emp effects and damage to all units in range of weapon
for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); ) {
// Special simulated crawler detonation location used, since it walks a bit
if (currentWeapon->getTypeID() == ITEM_CRAWLER)
distance = _ai->getDistance((*i)->getPosX(), (*i)->getPosY(), currentTarget->getPosX(), currentTarget->getPosY());
// Normal detonation location used here
else {
distance = _ai->getDistance((*i)->getPosX(), (*i)->getPosY(), targetCoords->x, targetCoords->y);
}
if (distance < currentWeapon->getRadius()) {
// Apply damage
thisDamage = currentWeapon->getDamage();
if ((AAcounter != 3) && (currentWeapon->getTypeID() == ITEM_CLUSTER))
thisDamage = 0;
if (!_ai->_vm->_rnd.getRandomNumber(4))
currentWeapon->setTypeID(ITEM_MINE);
(*i)->setDamage((int)thisDamage);
// Apply emp effect
if (currentWeapon->getTypeID() == ITEM_EMP) {
(*i)->setState(DUS_OFF);
}
// Remove destroyed defenses
if ((*i)->getArmor() <= 0) {
delete *i;
i = thisEnemyDefenses.erase(i);
} else {
i++;
}
} else {
i++;
}
}
}
retSortie->setEnemyDefenses(thisEnemyDefenses);
delete targetCoords;
delete currentWeapon;
return retSortie;
}
float Sortie::calcH() {
float retValue = 0;
Common::Array<DefenseUnit *> thisEnemyDefenses = getEnemyDefenses();
for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) {
if ((*i)->getState() == DUS_ON) {
switch ((*i)->getType()) {
case DUT_ANTI_AIR:
retValue += 1; // Is it bug in the original? Fixing it may break replay compatibility
// fall through
case DUT_MINE:
retValue += 1;
break;
case DUT_SHIELD:
retValue += 1;
break;
default:
break;
}
}
}
return retValue;
}
int Sortie::checkSuccess() {
if (!_enemyDefenses.size())
return SUCCESS;
int targetX = getTargetPosX();
int targetY = getTargetPosY();
int targetCheck = 0;
for (Common::Array<DefenseUnit *>::iterator i = _enemyDefenses.begin(); i != _enemyDefenses.end(); i++) {
if (((*i)->getState() == DUS_ON) && ((*i)->getType() != DUT_HUB)) {
return 0;
}
if (((*i)->getPosX() == targetX) && ((*i)->getPosY() == targetY)) targetCheck = 1;
}
if (!targetCheck)
return SUCCESS;
// If shot pos == target pos return SUCCESS;
if ((targetX == getShotPosX()) && (getTargetPosY() == getShotPosY())) {
return SUCCESS;
}
return 0;
}
float Sortie::calcT() {
return (checkSuccess() != SUCCESS) ? (getG() + calcH()) : SUCCESS;
}
IContainedObject *Sortie::duplicate() {
return this;
}
void Sortie::printEnemyDefenses() {
for (Common::Array<DefenseUnit *>::iterator i = _enemyDefenses.begin(); i != _enemyDefenses.end(); i++) {
debugC(DEBUG_MOONBASE_AI, "Unit %d - Type: %d, Armor: %d, Status: %d", (*i)->getID(), (*i)->getType(), static_cast<int>((*i)->getArmor()), (*i)->getState());
}
}
Defender::Defender(AI *ai) : _ai(ai) {
_sourceX = _sourceY = 0;
_targetX = _targetY = 0;
_sourceUnit = 0;
_power = 0;
_angle = 0;
_unit = 0;
}
int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) {
int currentPlayer = _ai->getCurrentPlayer();
//get list of near hubs
int unitsArray = _ai->getUnitsWithinRadius(targetX + 5, targetY, 480);
const int NUM_HUBS = 10;
//Order on dist
int hubArray[NUM_HUBS] = { 0 };
int hubIndex = 0;
for (int i = 0; i < 200; i++) {
int thisUnit = _ai->_vm->_moonbase->readFromArray(unitsArray, 0, i);
if (thisUnit) {
if (((_ai->getBuildingType(thisUnit) == BUILDING_MAIN_BASE) || (_ai->getBuildingType(thisUnit) == BUILDING_OFFENSIVE_LAUNCHER)) && (_ai->getBuildingOwner(thisUnit) == currentPlayer)) {
for (int j = 0; j < NUM_HUBS; j++) {
if (hubArray[j]) {
int distCurrent = _ai->getDistance(targetX, targetY, _ai->getHubX(thisUnit), _ai->getHubY(thisUnit));
int distSaved = _ai->getDistance(targetX, targetY, _ai->getHubX(hubArray[j]), _ai->getHubY(hubArray[j]));
if (distCurrent < distSaved) {
hubArray[hubIndex] = hubArray[j];
hubArray[j] = thisUnit;
hubIndex++;
j = 100;
}
} else {
hubArray[j] = thisUnit;
hubIndex++;
j = 100;
}
}
}
}
if (hubIndex >= NUM_HUBS) {
hubIndex = NUM_HUBS;
i = 200;
}
}
_ai->_vm->_moonbase->deallocateArray(unitsArray);
//Check if repair is needed
int targetUnit = _ai->getClosestUnit(targetX + 5, targetY, 15, currentPlayer, 1, 0, 0, 0);
if (targetUnit && (targetUnit != BUILDING_CRAWLER) && (_ai->getBuildingTeam(targetUnit) == _ai->getPlayerTeam(currentPlayer))) {
int armor = _ai->getBuildingArmor(targetUnit);
if (armor < _ai->getBuildingMaxArmor(targetUnit)) {
unitsArray = _ai->getUnitsWithinRadius(targetX + 5, targetY, 170);
int defCount = 0;
for (int i = 0; i < 200; i++) {
int thisUnit = _ai->_vm->_moonbase->readFromArray(unitsArray, 0, i);
if (thisUnit) {
if (((_ai->getBuildingType(thisUnit) == BUILDING_SHIELD) || (_ai->getBuildingType(thisUnit) == BUILDING_ANTI_AIR)) && (_ai->getBuildingOwner(thisUnit) == currentPlayer) && (_ai->getBuildingState(thisUnit) == 0)) {
defCount++;
i = 200;
}
}
}
_ai->_vm->_moonbase->deallocateArray(unitsArray);
if (defCount) {
//repair
int hubUnit = _ai->getClosestUnit(targetX, targetY, 480, currentPlayer, 1, BUILDING_MAIN_BASE, 1, 110);
if (hubUnit && (hubUnit != targetUnit)) {
int powAngle = abs(_ai->getPowerAngleFromPoint(_ai->getHubX(hubUnit), _ai->getHubY(hubUnit), targetX, targetY, 20));
int power = powAngle / 360;
int angle = powAngle - (power * 360);
setTargetX(targetX);
setTargetY(targetY);
setSourceUnit(hubUnit);
setUnit(ITEM_REPAIR);
setPower(power);
setAngle(angle);
return 1;
}
}
}
}
//For each hub
for (int i = 0; i < MIN(NUM_HUBS, hubIndex); i++) {
int hubX = _ai->getHubX(hubArray[i]);
int hubY = _ai->getHubY(hubArray[i]);
//get angle to hub
int directAngleToHub = 0;
//If this hub is the target
if ((hubX == targetX) && (hubY == targetY)) {
//make the angle seed point at the closest enemy
int enemyUnit = _ai->getClosestUnit(hubX, hubY, _ai->getMaxX(), currentPlayer, 0, 0, 0);
directAngleToHub = _ai->calcAngle(targetX, targetY, _ai->getHubX(enemyUnit), _ai->getHubY(enemyUnit));
} else {
directAngleToHub = _ai->calcAngle(targetX, targetY, hubX, hubY);
}
//Number of random chances to land
for (int j = 0; j < 3; j++) {
//Pick random angle and dist within semicircle (-90 to +90) and (40 to 150)
int randAngle = directAngleToHub + _ai->_vm->_rnd.getRandomNumber(179) - 90;
int randDist = _ai->_vm->_rnd.getRandomNumber(109) + 40;
int x = (int)(targetX + randDist * cos(_ai->degToRad(randAngle)));
int y = (int)(targetY + randDist * sin(_ai->degToRad(randAngle)));
int powAngle = _ai->getPowerAngleFromPoint(hubX, hubY, x, y, 20);
if (powAngle < 0)
continue;
int power = powAngle / 360;
int angle = powAngle - (power * 360);
int coords = 0;
coords = _ai->simulateBuildingLaunch(hubX, hubY, power, angle, 100, 0);
//if valid, return
if (coords > 0) {
//debugC(DEBUG_MOONBASE_AI, "The prospective launching hub for this defensive unit is: %d", hubArray[i]);
setSourceX(hubX);
setSourceY(hubY);
setTargetX((x + _ai->getMaxX()) % _ai->getMaxX());
setTargetY((y + _ai->getMaxY()) % _ai->getMaxY());
setSourceUnit(hubArray[i]);
int unitsArray2 = _ai->getUnitsWithinRadius(targetX + 5, targetY, 200);
int shieldCount = 0;
for (int k = 0; k < 200; k++) {
int thisUnit = _ai->_vm->_moonbase->readFromArray(unitsArray2, 0, k);
if (thisUnit) {
if ((_ai->getBuildingType(thisUnit) == BUILDING_SHIELD) && (_ai->getBuildingOwner(thisUnit) == currentPlayer))
shieldCount++;
if ((_ai->getBuildingType(thisUnit) == BUILDING_BRIDGE) && (_ai->getBuildingOwner(thisUnit) == currentPlayer)) {
shieldCount--;
shieldCount = MAX(-1, shieldCount);
}
}
}
if ((_ai->_vm->_rnd.getRandomNumber((int)pow(3.0f, shieldCount + 1) - 1) == 0) && (_ai->getPlayerEnergy() > 6))
setUnit(ITEM_SHIELD);
else
setUnit(ITEM_ANTIAIR);
setPower(power);
setAngle(angle);
_ai->_vm->_moonbase->deallocateArray(unitsArray2);
return 1;
}
if (coords < 0) {
//drop a bridge for the cord
int yCoord = -coords / _ai->getMaxX();
int xCoord = -coords - (yCoord * _ai->getMaxX());
if (_ai->checkIfWaterState(xCoord, yCoord)) {
int terrainSquareSize = _ai->getTerrainSquareSize();
xCoord = ((xCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2));
yCoord = ((yCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2));
int xDist = xCoord - x;
int yDist = yCoord - y;
x = (int)(xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1))));
y = (int)(yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1))));
setTargetX(x);
setTargetY(y);
int nextUnit = _ai->getClosestUnit(x, y, 480, _ai->getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 120);
powAngle = _ai->getPowerAngleFromPoint(_ai->getHubX(nextUnit), _ai->getHubY(nextUnit), x, y, 15);
powAngle = abs(powAngle);
power = powAngle / 360;
angle = powAngle - (power * 360);
setSourceUnit(nextUnit);
setUnit(ITEM_BRIDGE);
setPower(power);
setAngle(angle);
return 1;
}
}
}
}
// Else create new hub
int count = 0;
int coords = 0;
if (hubIndex == 0) return -3;
do {
int sourceHub = hubArray[_ai->_vm->_rnd.getRandomNumber(hubIndex - 1)];
setSourceX(_ai->getHubX(sourceHub));
setSourceY(_ai->getHubY(sourceHub));
setSourceUnit(sourceHub);
setUnit(ITEM_HUB);
setPower(_ai->_vm->_rnd.getRandomNumber(299) + 200);
setAngle(_ai->_vm->_rnd.getRandomNumber(359));
count++;
if (count > (NUM_HUBS * 3)) break;
coords = _ai->simulateBuildingLaunch(getSourceX(), getSourceY(), getPower(), getAngle(), 100, 0);
} while (coords <= 0);
if (coords > 0) {
setTargetX(coords % _ai->getMaxX());
setTargetY(coords / _ai->getMaxX());
} else {
setTargetX(0);
setTargetY(0);
}
return -1;
}
} // End of namespace Scumm

View File

@@ -0,0 +1,151 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_AI_TARGETACQUISITION_H
#define SCUMM_HE_MOONBASE_AI_TARGETACQUISITION_H
#include "scumm/he/moonbase/ai_defenseunit.h"
#include "scumm/he/moonbase/ai_node.h"
#include "scumm/he/moonbase/ai_tree.h"
namespace Scumm {
const int NUM_IMPT_UNITS = 3;
const int NUM_SHOT_POSITIONS = 1;
const int NUM_WEAPONS = 3;
class Sortie : public IContainedObject {
private:
static int _sSourceX;
static int _sSourceY;
static int _sTargetX;
static int _sTargetY;
int _unitType;
int _shotPosX, _shotPosY;
Common::Array<DefenseUnit *> _enemyDefenses;
AI *_ai;
public:
Sortie(AI *ai) { _ai = ai; _unitType = 0; _shotPosX = _shotPosY = 0; }
~Sortie() override;
static void setSourcePos(int x, int y) {
_sSourceX = x;
_sSourceY = y;
}
static void setTargetPos(int x, int y) {
_sTargetX = x;
_sTargetY = y;
}
void setUnitType(int unitType) { _unitType = unitType; }
void setShotPosX(int shotPosX) { _shotPosX = shotPosX; }
void setShotPosY(int shotPosY) { _shotPosY = shotPosY; }
void setShotPos(int shotPosX, int shotPosY) {
_shotPosX = shotPosX;
_shotPosY = shotPosY;
}
void setEnemyDefenses(Common::Array<DefenseUnit *> enemyDefenses) {
_enemyDefenses = enemyDefenses;
}
void setEnemyDefenses(int enemyDefensesScummArray, int defendX, int defendY);
void printEnemyDefenses();
static int getSourcePosX() { return _sSourceX; }
static int getSourcePosY() { return _sSourceY; }
static int getTargetPosX() { return _sTargetX; }
static int getTargetPosY() { return _sTargetY; }
int getUnitType() const { return _unitType; }
int getShotPosX() const { return _shotPosX; }
int getShotPosY() const { return _shotPosY; }
int *getShotPos() const;
Common::Array<DefenseUnit *> getEnemyDefenses() const { return _enemyDefenses; }
IContainedObject *duplicate() override;
int numChildrenToGen() override;
IContainedObject *createChildObj(int, int &completionFlag) override;
float calcH() override;
int checkSuccess() override;
float calcT() override;
};
class Defender {
private:
int _sourceX;
int _sourceY;
int _targetX;
int _targetY;
int _sourceUnit;
int _power;
int _angle;
int _unit;
AI *_ai;
public:
Defender(AI *ai);
void setSourceX(int sourceX) { _sourceX = sourceX; }
void setSourceY(int sourceY) { _sourceY = sourceY; }
void setTargetX(int targetX) { _targetX = targetX; }
void setTargetY(int targetY) { _targetY = targetY; }
void setSourceUnit(int sourceUnit) { _sourceUnit = sourceUnit; }
void setPower(int power) { _power = power; }
void setAngle(int angle) { _angle = angle; }
void setUnit(int unit) { _unit = unit; }
int getSourceX() const { return _sourceX; }
int getSourceY() const { return _sourceY; }
int getTargetX() const { return _targetX; }
int getTargetY() const { return _targetY; }
int getSourceUnit() const { return _sourceUnit; }
int getPower() const { return _power; }
int getAngle() const { return _angle; }
int getUnit() const { return _unit; }
int calculateDefenseUnitPosition(int targetX, int targetY, int index);
};
class defenseUnitCompare {
public:
bool operator()(DefenseUnit *x, DefenseUnit *y) {
//disabled units go at the end
if (x->getState() == DUS_OFF) {
debugC(DEBUG_MOONBASE_AI, "OFF");
return 0;
}
return x->getDistanceTo() < y->getDistanceTo();
}
};
} // End of namespace Scumm
#endif

View File

@@ -0,0 +1,278 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "scumm/he/intern_he.h"
#include "scumm/he/moonbase/moonbase.h"
#include "scumm/he/moonbase/ai_traveller.h"
#include "scumm/he/moonbase/ai_main.h"
namespace Scumm {
int Traveller::_targetPosX = 0;
int Traveller::_targetPosY = 0;
int Traveller::_maxDist = 0;
int Traveller::_numToGen = 0;
int Traveller::_sizeAngleStep = 0;
Traveller::Traveller(AI *ai) : _ai(ai) {
_waterFlag = 0;
setValueG(0);
unsetDisabled();
_sourceHub = 0;
_angleTo = 0;
_powerTo = 0;
_waterSourceX = 0;
_waterSourceY = 0;
_waterDestX = 0;
_waterDestY = 0;
_posX = _posY = 0;
}
Traveller::Traveller(int originX, int originY, AI *ai) : _ai(ai) {
_waterFlag = 0;
setValueG(0);
unsetDisabled();
_posX = originX;
_posY = originY;
_sourceHub = 0;
_angleTo = 0;
_powerTo = 0;
_waterSourceX = 0;
_waterSourceY = 0;
_waterDestX = 0;
_waterDestY = 0;
}
void Traveller::adjustPosX(int offsetX) {
int maxX = _ai->getMaxX();
int deltaX = _posX + offsetX;
if (deltaX < 0) _posX = maxX + deltaX;
else if (deltaX > maxX) _posX = deltaX - maxX;
else _posX = deltaX;
}
void Traveller::adjustPosY(int offsetY) {
int maxY = _ai->getMaxX();
int deltaY = _posY + offsetY;
if (deltaY < 0) _posY = maxY + deltaY;
else if (deltaY > maxY) _posY = deltaY - maxY;
else _posY = deltaY;
}
void Traveller::adjustXY(int offsetX, int offsetY) {
adjustPosX(offsetX);
adjustPosY(offsetY);
}
float Traveller::calcH() {
float retVal = 0;
// Calc dist from here to target
retVal = _ai->getDistance(_posX, _posY, _targetPosX, _targetPosY);
// Divide by _maxDist to get minimum number of jumps to goal
retVal /= static_cast<float>(_maxDist);
return retVal * 2.0;
}
int Traveller::numChildrenToGen() {
if (!_numToGen)
_numToGen = _ai->getAnimSpeed() + 2;
return _numToGen;
}
IContainedObject *Traveller::createChildObj(int index, int &completionFlag) {
//static int nodeCount = 0;
static int completionState = 1;
//if (!index) nodeCount = 0;
//nodeCount++;
Traveller *retTraveller = new Traveller(_ai);
static int dir, angle, power;
if (completionState) {
// Calculate angle between here and target
int directAngle = 0;
if (_ai->getEnergyHogType())
directAngle = _ai->calcAngle(_posX, _posY, _targetPosX, _targetPosY, 1);
else
directAngle = _ai->calcAngle(_posX, _posY, _targetPosX, _targetPosY);
// Calculate the offset angle for this index
if (!_sizeAngleStep)
_sizeAngleStep = 52 - (_ai->getAnimSpeed() * 7);
dir = _sizeAngleStep * ((static_cast<int>(index / NUM_POWER_STEPS) + 1) >> 1);
// Calculate the sign value for the offset for this index
int orientation = dir * (((static_cast<int>(index / NUM_POWER_STEPS) % 2) << 1) - 1);
// Add the offset angle to the direct angle to target
angle = orientation + directAngle;
// Calculate power for this index
int maxPower = 0;
int directDist = _ai->getDistance(_posX, _posY, _targetPosX, _targetPosY);
if (directDist > _maxDist + 120)
maxPower = _ai->getMaxPower();
else
maxPower = (int)((static_cast<float>(directDist) / static_cast<float>(_maxDist + 120)) * _ai->getMaxPower());
maxPower -= 70;
power = (int)(maxPower * (1 - ((index % NUM_POWER_STEPS) * SIZE_POWER_STEP)));
}
retTraveller->setAngleTo(angle);
retTraveller->setPowerTo(power);
// Set this object's position to the new one determined by the power and angle from above
static int lastSuccessful = 0;
int coords = 0;
if (!(index % NUM_POWER_STEPS) || (!lastSuccessful)) {
coords = _ai->simulateBuildingLaunch(_posX, _posY, power, angle, 10, 0);
lastSuccessful = 0;
} else {
completionState = 1;
lastSuccessful = 0;
}
if (!coords) {
completionFlag = 0;
completionState = 0;
delete retTraveller;
return NULL;
} else {
completionFlag = 1;
completionState = 1;
}
int whoseTurn = _ai->getCurrentPlayer();
int maxX = _ai->getMaxX();
// Check new position to see if landing is clear
if (coords > 0) {
int yCoord = coords / maxX;
int xCoord = coords - (yCoord * maxX);
int terrain = _ai->getTerrain(xCoord, yCoord);
assert(terrain == TERRAIN_TYPE_GOOD);
float pwr = _ai->getMinPower() * .3;
float cosine = cos((static_cast<float>(angle) / 360) * (2 * M_PI));
float sine = sin((static_cast<float>(angle) / 360) * (2 * M_PI));
int xParam = (int)(xCoord + (pwr * cosine));
int yParam = (int)(yCoord + (pwr * sine));
if (xParam < 0)
xParam += _ai->getMaxX();
else if (xParam > _ai->getMaxX())
xParam -= _ai->getMaxX();
if (yParam < 0)
yParam += _ai->getMaxY();
else if (yParam > _ai->getMaxY())
yParam -= _ai->getMaxY();
if (_ai->checkIfWaterState(xParam, yParam)) {
delete retTraveller;
return NULL;
}
retTraveller->setPosY(yCoord);
retTraveller->setPosX(xCoord);
// Iterate through the previous action list, making sure this one isn't on it
for (Common::Array<int>::iterator i = (_ai->_lastXCoord[whoseTurn]).begin(), j = (_ai->_lastYCoord[whoseTurn]).begin(); i != (_ai->_lastXCoord[whoseTurn]).end(); i++, j++) {
// Check if this shot is the same as the last time we tried
if ((*i == retTraveller->getPosX()) && (*j == retTraveller->getPosY())) {
retTraveller->setDisabled();
delete retTraveller;
return NULL;
}
}
retTraveller->setValueG(getG() + 7 + (dir * DIRECTION_WEIGHT));
lastSuccessful = 1;
} else {
int yCoord = -coords / maxX;
int xCoord = -coords - (yCoord * maxX);
// If landing fault is because of water, add 1 extra to g and turn on water flag. Also set coords, and adjust power to water fault location
if (_ai->checkIfWaterState(xCoord, yCoord)) {
int terrainSquareSize = _ai->getTerrainSquareSize();
xCoord = ((xCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2));
yCoord = ((yCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2));
int xDist = xCoord - _posX;
int yDist = yCoord - _posY;
retTraveller->setPosX((int)(xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1)))));
retTraveller->setPosY((int)(yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1)))));
int closestHub = _ai->getClosestUnit(retTraveller->getPosX(), retTraveller->getPosY(), _ai->getMaxX(), _ai->getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110);
retTraveller->setWaterSourceX(_ai->getHubX(closestHub));
retTraveller->setWaterSourceY(_ai->getHubY(closestHub));
retTraveller->setWaterDestX(retTraveller->getPosX());
retTraveller->setWaterDestY(retTraveller->getPosY());
retTraveller->setPowerTo(power);
retTraveller->setAngleTo(angle);
retTraveller->setValueG(getG() + 10 + (dir * DIRECTION_WEIGHT));
retTraveller->enableWaterFlag();
} else {
// If not, set G to highest value
retTraveller->setDisabled();
delete retTraveller;
return NULL;
}
}
return retTraveller;
}
int Traveller::checkSuccess() {
if (_ai->getDistance(_posX + 1, _posY, _targetPosX, _targetPosY) < _maxDist)
return SUCCESS;
return 0;
}
float Traveller::calcT() {
assert(!_disabled);
if (_disabled) return FAILURE;
return (checkSuccess() != SUCCESS) ? (getG() + calcH()) : SUCCESS;
}
} // End of namespace Scumm

View File

@@ -0,0 +1,122 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_AI_TRAVELER_H
#define SCUMM_HE_MOONBASE_AI_TRAVELER_H
#include "scumm/he/moonbase/ai_node.h"
namespace Scumm {
const int NUM_TO_GEN = 9;
const int NUM_POWER_STEPS = 3;
const double SIZE_POWER_STEP = .15;
const int SIZE_ANGLE_STEP = 45;
const int VARIATION_EXTENT = 3;
const int DIRECTION_WEIGHT = 5;
class Traveller : public IContainedObject {
private:
static int _targetPosX;
static int _targetPosY;
static int _maxDist;
static int _numToGen;
static int _sizeAngleStep;
int _sourceHub;
int _posX;
int _posY;
int _angleTo;
int _powerTo;
int _disabled;
int _waterFlag;
int _waterSourceX;
int _waterSourceY;
int _waterDestX;
int _waterDestY;
AI *_ai;
protected:
float calcH() override;
public:
Traveller(AI *ai);
Traveller(int originX, int originY, AI *ai);
~Traveller() override {}
IContainedObject *duplicate() override { return this; }
static void setTargetPosX(int posX) { _targetPosX = posX; }
static void setTargetPosY(int posY) { _targetPosY = posY; }
static void setMaxDist(int maxDist) { _maxDist = maxDist; }
void setSourceHub(int sourceHub) { _sourceHub = sourceHub; }
void setPosX(int posX) { _posX = posX; }
void setPosY(int posY) { _posY = posY; }
void setAngleTo(int angleTo) { _angleTo = angleTo; }
void setPowerTo(int powerTo) { _powerTo = powerTo; }
void setWaterSourceX(int waterSourceX) { _waterSourceX = waterSourceX; }
void setWaterSourceY(int waterSourceY) { _waterSourceY = waterSourceY; }
void setWaterDestX(int waterDestX) { _waterDestX = waterDestX; }
void setWaterDestY(int waterDestY) { _waterDestY = waterDestY; }
int getSourceHub() const { return _sourceHub; }
int getPosX() const { return _posX; }
int getPosY() const { return _posY; }
int getAngleTo() const { return _angleTo; }
int getPowerTo() const { return _powerTo; }
int getWaterSourceX() const { return _waterSourceX; }
int getWaterSourceY() const { return _waterSourceY; }
int getWaterDestX() const { return _waterDestX; }
int getWaterDestY() const { return _waterDestY; }
void setDisabled() { _disabled = 1; }
void unsetDisabled() { _disabled = 0; }
int getDisabled() { return _disabled; }
void adjustPosX(int offsetX);
void adjustPosY(int offsetY);
void adjustXY(int offsetX, int offsetY);
void enableWaterFlag() { _waterFlag = 1; }
void disableWaterFlag() { _waterFlag = 0; }
int getWaterFlag() const { return _waterFlag; }
int numChildrenToGen() override;
IContainedObject *createChildObj(int, int &) override;
int checkSuccess() override;
float calcT() override;
};
} // End of namespace Scumm
#endif

View File

@@ -0,0 +1,244 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "scumm/he/intern_he.h"
#include "scumm/he/moonbase/moonbase.h"
#include "scumm/he/moonbase/ai_tree.h"
#include "scumm/he/moonbase/ai_main.h"
namespace Scumm {
static int compareTreeNodes(const void *a, const void *b) {
if (((const TreeNode *)a)->value < ((const TreeNode *)b)->value)
return -1;
else if (((const TreeNode *)a)->value > ((const TreeNode *)b)->value)
return 1;
else
return 0;
}
Tree::Tree(AI *ai) : _ai(ai) {
pBaseNode = new Node;
_maxDepth = MAX_DEPTH;
_maxNodes = MAX_NODES;
_currentNode = nullptr;
_currentChildIndex = 0;
_currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes);
}
Tree::Tree(IContainedObject *contents, AI *ai) : _ai(ai) {
pBaseNode = new Node;
pBaseNode->setContainedObject(contents);
_maxDepth = MAX_DEPTH;
_maxNodes = MAX_NODES;
_currentNode = nullptr;
_currentChildIndex = 0;
_currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes);
}
Tree::Tree(IContainedObject *contents, int maxDepth, AI *ai) : _ai(ai) {
pBaseNode = new Node;
pBaseNode->setContainedObject(contents);
_maxDepth = maxDepth;
_maxNodes = MAX_NODES;
_currentNode = nullptr;
_currentChildIndex = 0;
_currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes);
}
Tree::Tree(IContainedObject *contents, int maxDepth, int maxNodes, AI *ai) : _ai(ai) {
pBaseNode = new Node;
pBaseNode->setContainedObject(contents);
_maxDepth = maxDepth;
_maxNodes = maxNodes;
_currentNode = nullptr;
_currentChildIndex = 0;
_currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes);
}
void Tree::duplicateTree(Node *sourceNode, Node *destNode) {
Common::Array<Node *> vUnvisited = sourceNode->getChildren();
while (vUnvisited.size()) {
Node *newNode = new Node(*(vUnvisited.end()));
newNode->setParent(destNode);
(destNode->getChildren()).push_back(newNode);
duplicateTree(*(vUnvisited.end()), newNode);
vUnvisited.pop_back();
}
}
Tree::Tree(const Tree *sourceTree, AI *ai) : _ai(ai) {
pBaseNode = new Node(sourceTree->getBaseNode());
_maxDepth = sourceTree->getMaxDepth();
_maxNodes = sourceTree->getMaxNodes();
_currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes);
_currentNode = nullptr;
_currentChildIndex = 0;
duplicateTree(sourceTree->getBaseNode(), pBaseNode);
}
Tree::~Tree() {
// Delete all nodes
Node *pNodeItr = pBaseNode;
// Depth first traversal of nodes to delete them
while (pNodeItr != nullptr) {
// If any children are left, move to one of them
if (!(pNodeItr->getChildren().empty())) {
pNodeItr = pNodeItr->popChild();
} else {
// Delete this node, and move up to the parent for further processing
Node *pTemp = pNodeItr;
pNodeItr = pNodeItr->getParent();
delete pTemp;
pTemp = nullptr;
}
}
delete _currentMap;
}
Node *Tree::aStarSearch() {
Common::SortedArray<TreeNode *> mmfpOpen(compareTreeNodes);
Node *currentNode = nullptr;
float currentT;
Node *retNode = nullptr;
float temp = pBaseNode->getContainedObject()->calcT();
if (static_cast<int>(temp) != SUCCESS) {
mmfpOpen.insert(new TreeNode(pBaseNode->getObjectT(), pBaseNode));
while (mmfpOpen.size() && (retNode == nullptr)) {
currentNode = mmfpOpen.front()->node;
mmfpOpen.erase(mmfpOpen.begin());
if ((currentNode->getDepth() < _maxDepth) && (Node::getNodeCount() < _maxNodes)) {
// Generate nodes
Common::Array<Node *> vChildren = currentNode->getChildren();
for (Common::Array<Node *>::iterator i = vChildren.begin(); i != vChildren.end(); i++) {
IContainedObject *pTemp = (*i)->getContainedObject();
currentT = pTemp->calcT();
if (currentT == SUCCESS)
retNode = *i;
else
mmfpOpen.insert(new TreeNode(currentT, (*i)));
}
} else {
retNode = currentNode;
}
}
} else {
retNode = pBaseNode;
}
return retNode;
}
Node *Tree::aStarSearch_singlePassInit() {
Node *retNode = nullptr;
_currentChildIndex = 1;
float temp = pBaseNode->getContainedObject()->calcT();
if (static_cast<int>(temp) != SUCCESS) {
_currentMap->insert(new TreeNode(pBaseNode->getObjectT(), pBaseNode));
} else {
retNode = pBaseNode;
}
return retNode;
}
Node *Tree::aStarSearch_singlePass() {
float currentT = 0.0;
Node *retNode = nullptr;
static int maxTime = 0;
if (_currentChildIndex == 1) {
maxTime = _ai->getPlayerMaxTime();
}
if (_currentChildIndex) {
if (!(_currentMap->size())) {
retNode = _currentNode;
return retNode;
}
_currentNode = _currentMap->front()->node;
_currentMap->erase(_currentMap->begin());
}
if ((_currentNode->getDepth() < _maxDepth) && (Node::getNodeCount() < _maxNodes) && ((!maxTime) || (_ai->getTimerValue(3) < maxTime))) {
// Generate nodes
_currentChildIndex = _currentNode->generateChildren();
if (_currentChildIndex) {
Common::Array<Node *> vChildren = _currentNode->getChildren();
if (!vChildren.size() && !_currentMap->size()) {
_currentChildIndex = 0;
retNode = _currentNode;
}
for (Common::Array<Node *>::iterator i = vChildren.begin(); i != vChildren.end(); i++) {
IContainedObject *pTemp = (*i)->getContainedObject();
currentT = pTemp->calcT();
if (currentT == SUCCESS) {
retNode = *i;
i = vChildren.end() - 1;
} else {
_currentMap->insert(new TreeNode(currentT, (*i)));
}
}
if (!(_currentMap->size()) && (currentT != SUCCESS)) {
assert(_currentNode != nullptr);
retNode = _currentNode;
}
}
} else {
retNode = _currentNode;
}
return retNode;
}
int Tree::IsBaseNode(Node *thisNode) {
return (thisNode == pBaseNode);
}
} // End of namespace Scumm

View File

@@ -0,0 +1,83 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_AI_TREE_H
#define SCUMM_HE_MOONBASE_AI_TREE_H
#include "common/array.h"
#include "scumm/he/moonbase/ai_node.h"
namespace Scumm {
const int MAX_DEPTH = 100;
const int MAX_NODES = 1000000;
class AI;
struct TreeNode {
float value;
Node *node;
TreeNode(float v, Node *n) { value = v; node = n; }
};
class Tree {
private:
Node *pBaseNode;
int _maxDepth;
int _maxNodes;
int _currentChildIndex;
Common::SortedArray<TreeNode *> *_currentMap;
Node *_currentNode;
AI *_ai;
public:
Tree(AI *ai);
Tree(IContainedObject *contents, AI *ai);
Tree(IContainedObject *contents, int maxDepth, AI *ai);
Tree(IContainedObject *contents, int maxDepth, int maxNodes, AI *ai);
Tree(const Tree *sourceTree, AI *ai);
~Tree();
void duplicateTree(Node *sourceNode, Node *destNode);
Node *getBaseNode() const { return pBaseNode; }
void setMaxDepth(int maxDepth) { _maxDepth = maxDepth; }
int getMaxDepth() const { return _maxDepth; }
void setMaxNodes(int maxNodes) { _maxNodes = maxNodes; }
int getMaxNodes() const { return _maxNodes; }
Node *aStarSearch();
Node *aStarSearch_singlePassInit();
Node *aStarSearch_singlePass();
int IsBaseNode(Node *thisNode);
};
} // End of namespace Scumm
#endif

View File

@@ -0,0 +1,176 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/textconsole.h"
#include "scumm/scumm.h"
#include "scumm/he/moonbase/ai_types.h"
namespace Scumm {
AIEntity::AIEntity(int id) {
switch (id) {
default:
case BRUTAKAS:
debugC(DEBUG_MOONBASE_AI, "BRUTAKAS");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "BRUTAKAS", 64);
_behaviorVariation = AI_VAR_SMALL;
_targetVariation = AI_VAR_SMALL;
_angleVariation = AI_VAR_SMALL;
_powerVariation = AI_VAR_SMALL;
break;
case AGI:
debugC(DEBUG_MOONBASE_AI, "Agi");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "Agi", 64);
_behaviorVariation = AI_VAR_SMALL;
_targetVariation = AI_VAR_MEDIUM;
_angleVariation = AI_VAR_MEDIUM;
_powerVariation = AI_VAR_LARGE;
break;
case EL_GATO:
debugC(DEBUG_MOONBASE_AI, "El Gato de la Noche");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "El Gato de la Noche", 64);
_behaviorVariation = AI_VAR_SMALL;
_targetVariation = AI_VAR_SMALL;
_angleVariation = AI_VAR_SMALL;
_powerVariation = AI_VAR_MEDIUM;
break;
case PIXELAHT:
debugC(DEBUG_MOONBASE_AI, "Pixelaht");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "Pixelaht", 64);
_behaviorVariation = AI_VAR_SMALL;
_targetVariation = AI_VAR_LARGE;
_angleVariation = AI_VAR_MEDIUM;
_powerVariation = AI_VAR_SMALL;
break;
case CYBALL:
debugC(DEBUG_MOONBASE_AI, "cYbaLL");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "cYbaLL", 64);
_behaviorVariation = AI_VAR_LARGE;
_targetVariation = AI_VAR_LARGE;
_angleVariation = AI_VAR_SMALL;
_powerVariation = AI_VAR_SMALL;
break;
case NEEP:
debugC(DEBUG_MOONBASE_AI, "Neep! Neep!");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "Neep! Neep!", 64);
_behaviorVariation = AI_VAR_MEDIUM;
_targetVariation = AI_VAR_SMALL;
_angleVariation = AI_VAR_SMALL;
_powerVariation = AI_VAR_LARGE;
break;
case WARCUPINE:
debugC(DEBUG_MOONBASE_AI, "WARcupine");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "WARcupine", 64);
_behaviorVariation = AI_VAR_SMALL;
_targetVariation = AI_VAR_SMALL;
_angleVariation = AI_VAR_LARGE;
_powerVariation = AI_VAR_MEDIUM;
break;
case AONE:
debugC(DEBUG_MOONBASE_AI, "aone");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "aone", 64);
_behaviorVariation = AI_VAR_MEDIUM;
_targetVariation = AI_VAR_MEDIUM;
_angleVariation = AI_VAR_MEDIUM;
_powerVariation = AI_VAR_MEDIUM;
break;
case SPANDO:
debugC(DEBUG_MOONBASE_AI, "S p a n d o");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "S p a n d o", 64);
_behaviorVariation = AI_VAR_LARGE;
_targetVariation = AI_VAR_LARGE;
_angleVariation = AI_VAR_SMALL;
_powerVariation = AI_VAR_SMALL;
break;
case ORBNU_LUNATEK:
debugC(DEBUG_MOONBASE_AI, "Bonur J Lunatek");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "Bonur J Lunatek", 64);
_behaviorVariation = AI_VAR_HUGE;
_targetVariation = AI_VAR_HUGE;
_angleVariation = AI_VAR_HUGE;
_powerVariation = AI_VAR_HUGE;
break;
case CRAWLER_CHUCKER:
debugC(DEBUG_MOONBASE_AI, "Le Chuckre des Crawlres");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "Le Chuckre des Crawlres", 64);
_behaviorVariation = AI_VAR_SMALL;
_targetVariation = AI_VAR_MEDIUM;
_angleVariation = AI_VAR_MEDIUM;
_powerVariation = AI_VAR_LARGE;
break;
case ENERGY_HOG:
debugC(DEBUG_MOONBASE_AI, "Energy Hog");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "Energy Hog\n", 64);
_behaviorVariation = AI_VAR_SMALL;
_targetVariation = AI_VAR_SMALL;
_angleVariation = AI_VAR_SMALL;
_powerVariation = AI_VAR_SMALL;
break;
case RANGER:
debugC(DEBUG_MOONBASE_AI, "Ranger");
_id = id;
_nameString = new char[64];
Common::strlcpy(_nameString, "Ranger\n", 64);
_behaviorVariation = AI_VAR_SMALL;
_targetVariation = AI_VAR_SMALL;
_angleVariation = AI_VAR_SMALL;
_powerVariation = AI_VAR_SMALL;
break;
}
}
} // End of namespace Scumm

View File

@@ -0,0 +1,96 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_AI_TYPES_H
#define SCUMM_HE_MOONBASE_AI_TYPES_H
namespace Scumm {
enum {
AGI = 1,
AONE = 2,
BRUTAKAS = 3,
CYBALL = 4,
EL_GATO = 5,
NEEP = 6,
ORBNU_LUNATEK = 7,
PIXELAHT = 8,
SPANDO = 9,
WARCUPINE = 10
};
enum {
CRAWLER_CHUCKER = 11,
ENERGY_HOG = 12,
RANGER = 13
};
enum {
AI_VAR_NONE = -1,
AI_VAR_SMALL = 0,
AI_VAR_MEDIUM = 1,
AI_VAR_LARGE = 2,
AI_VAR_HUGE = 5
};
enum {
AI_VAR_BASE_BEHAVIOR = 10,
AI_VAR_BASE_TARGET = 10,
AI_VAR_BASE_ANGLE = 2,
AI_VAR_BASE_POWER = 5
};
class AIEntity {
private:
int _id;
char *_nameString;
int _behaviorVariation;
int _targetVariation;
int _angleVariation;
int _powerVariation;
public:
AIEntity(int id);
~AIEntity() {
if (_nameString) {
delete[] _nameString;
_nameString = 0;
}
}
int getID() const { return _id; }
char *getNameString() const { return _nameString; }
int getBehaviorVariation() const { return _behaviorVariation; }
int getTargetVariation() const { return _targetVariation; }
int getAngleVariation() const { return _angleVariation; }
int getPowerVariation() const { return _powerVariation; }
void setID(int id) { _id = id; }
void setNameString(char *nameString) { _nameString = nameString; }
void setBehaviorVariation(int behaviorVariation) { _behaviorVariation = behaviorVariation; }
void setTargetVariation(int targetVariation) { _targetVariation = targetVariation; }
void setAngleVariation(int angleVariation) { _angleVariation = angleVariation; }
void setPowerVariation(int powerVariation) { _powerVariation = powerVariation; }
};
} // End of namespace Scumm
#endif

View File

@@ -0,0 +1,87 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "scumm/he/moonbase/ai_weapon.h"
#include "scumm/he/moonbase/ai_main.h"
namespace Scumm {
Weapon::Weapon(int typeID) { //, float damage, int radius)
switch (typeID) {
default:
case ITEM_BOMB:
becomeBomb();
break;
case ITEM_CLUSTER:
becomeCluster();
break;
case ITEM_CRAWLER:
becomeCrawler();
break;
case ITEM_EMP:
becomeEMP();
break;
case ITEM_SPIKE:
becomeSpike();
break;
}
}
void Weapon::becomeBomb() {
_typeID = ITEM_BOMB;
_damage = 3;
_radius = 30;
_cost = 1;
}
void Weapon::becomeCluster() {
_typeID = ITEM_CLUSTER;
_damage = 1.5;
_radius = 20;
_cost = 1;
}
void Weapon::becomeCrawler() {
_typeID = ITEM_CRAWLER;
_damage = 4;
_radius = 180;
_cost = 7;
}
void Weapon::becomeEMP() {
_typeID = ITEM_EMP;
_damage = .1f;
_radius = 215;
_cost = 3;
}
void Weapon::becomeSpike() {
_typeID = ITEM_SPIKE;
_damage = 6;
_radius = 180;
_cost = 3;
}
} // End of namespace Scumm

View File

@@ -0,0 +1,57 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_AI_WEAPON_H
#define SCUMM_HE_MOONBASE_AI_WEAPON_H
namespace Scumm {
class Weapon {
int _typeID = 0;
float _damage = 0.0f;
int _radius = 0;
int _cost = 0;
public:
Weapon() {}
Weapon(int typeID);
virtual ~Weapon() {}
void setTypeID(int typeID) { _typeID = typeID; }
void setDamage(float damage) { _damage = damage; }
void setRadius(int radius) { _radius = radius; }
void setCost(int cost) { _cost = cost; }
int getTypeID() { return _typeID; }
float getDamage() { return _damage; }
int getRadius() { return _radius; }
int getCost() { return _cost; }
void becomeBomb();
void becomeCluster();
void becomeCrawler();
void becomeEMP();
void becomeSpike();
};
} // End of namespace Scumm
#endif

View File

@@ -0,0 +1,377 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "common/translation.h"
#include "scumm/he/moonbase/map_main.h"
#include "scumm/he/moonbase/dialog-mapgenerator.h"
namespace Scumm {
enum {
kAlgorCmd = 'ALGR',
kAlgorRandCmd = 'ALRM',
kSizeCmd = 'SIZE',
kSizeRandCmd = 'SZRM',
kTileCmd = 'TILE',
kTileRandCmd = 'TLRM',
kEnergyCmd = 'ENGY',
kEnergyRandCmd = 'EGRM',
kTerrainCmd = 'TRIN',
kTerrainRandCmd = 'TRRM',
kWaterCmd = 'WTER',
kWaterRandCmd = 'WTRM',
kCancelCmd = 'CNCL',
kGenerateCmd = 'GNRT'
};
MapGeneratorDialog::MapGeneratorDialog(bool demo) : Dialog("MapGenerator"), _refreshing(false) {
// I18N: Random map generator for Moonbase Commander
_dialogTitle = new GUI::StaticTextWidget(this, "MapGenerator.Title", _("Random Map Options"));
_dialogTitle->setAlign(Graphics::kTextAlignCenter);
// I18N: Map generator algorithms
_algorDesc = new GUI::StaticTextWidget(this, "MapGenerator.Algorithm", _("Algorithm"));
_algorDesc->setAlign(Graphics::kTextAlignLeft);
_algorGroup = new GUI::RadiobuttonGroup(this, kAlgorCmd);
// I18N: Spiff algorithm
_algorSpiff = new GUI::RadiobuttonWidget(this, "MapGenerator.AlgorithmSpiff", _algorGroup, SPIFF_GEN, _("Spiff"));
// I18N: Katton algorithm
_algorSpiff = new GUI::RadiobuttonWidget(this, "MapGenerator.AlgorithmKatton", _algorGroup, KATTON_GEN, _("Katton"));
// I18N: Random algorithm
_algorRandom = new GUI::CheckboxWidget(this, "MapGenerator.AlgorithmRandom", _("Random"), _("Picks the map algorithm randomly."), kAlgorRandCmd);
// I18N: Map sizes
_sizeDesc = new GUI::StaticTextWidget(this, "MapGenerator.Size", _("Size"));
_sizeDesc->setAlign(Graphics::kTextAlignCenter);
_sizeGroup = new GUI::RadiobuttonGroup(this, kSizeCmd);
_sizeSmall = new GUI::RadiobuttonWidget(this, "MapGenerator.SizeSmall", _sizeGroup, 4, _("Small"));
_sizeMedium = new GUI::RadiobuttonWidget(this, "MapGenerator.SizeMedium", _sizeGroup, 5, _("Medium"));
_sizeLarge = new GUI::RadiobuttonWidget(this, "MapGenerator.SizeLarge", _sizeGroup, 6, _("Large"));
_sizeHuge = new GUI::RadiobuttonWidget(this, "MapGenerator.SizeHuge", _sizeGroup, 7, _("Huge"));
_sizeSAI = new GUI::RadiobuttonWidget(this, "MapGenerator.SizeSAI", _sizeGroup, 8, _("SAI"));
_sizeRidiculous = new GUI::RadiobuttonWidget(this, "MapGenerator.SizeRidiculous", _sizeGroup, 9, _("Ridiculous"));
_sizeMax = new GUI::RadiobuttonWidget(this, "MapGenerator.SizeMax", _sizeGroup, 10, _("Max"));
// I18N: Random map size
_sizeRandom = new GUI::CheckboxWidget(this, "MapGenerator.SizeRandom", _("Random"), _("Picks the map size randomly."), kSizeRandCmd);
// I18N: Map tile sets
_tileDesc = new GUI::StaticTextWidget(this, "MapGenerator.Tileset", _("Tileset"));
_tileDesc->setAlign(Graphics::kTextAlignCenter);
_tileGroup = new GUI::RadiobuttonGroup(this, kTileCmd);
_tileTerrandra = new GUI::RadiobuttonWidget(this, "MapGenerator.TilesetTerrandra", _tileGroup, 1, Common::U32String("Terrandra"));
_tileZanateros = new GUI::RadiobuttonWidget(this, "MapGenerator.TilesetZanateros", _tileGroup, 2, Common::U32String("Zanateros"));
// Demo version of the game only has tilesets 1, 2, 4 and 6. Don't create buttons for
// missing tiles.
if (!demo)
_tileDrijim = new GUI::RadiobuttonWidget(this, "MapGenerator.TilesetDaijim", _tileGroup, 3, Common::U32String("Daijim 3"));
_tileKyanite = new GUI::RadiobuttonWidget(this, "MapGenerator.TilesetKyanite", _tileGroup, 4, Common::U32String("Kyanite"));
if (!demo)
_tileEmerau = new GUI::RadiobuttonWidget(this, "MapGenerator.TilesetEmerau", _tileGroup, 5, Common::U32String("Emerau Glyph"));
_tileAblation = new GUI::RadiobuttonWidget(this, "MapGenerator.TilesetAblation", _tileGroup, 6, Common::U32String("Ablation Land"));
// I18N: Random tileset
_tileRandom = new GUI::CheckboxWidget(this, "MapGenerator.TilesetRandom", _("Random"), _("Picks the map tileset randomly."), kTileRandCmd);
// I18N: Percentage of energy pools
_energyDesc = new GUI::StaticTextWidget(this, "MapGenerator.Energy", _("Energy"));
_energyDesc->setAlign(Graphics::kTextAlignCenter);
_energySlider = new GUI::SliderWidget(this, "MapGenerator.EnergySlider", Common::U32String(), kEnergyCmd);
_energySlider->setMinValue(0); _energySlider->setMaxValue(6);
// I18N: Energy slider label
_energyLabel = new GUI::StaticTextWidget(this, "MapGenerator.EnergySliderLabel", _("Scarce - Lots"), Common::U32String());
_energyLabel->setAlign(Graphics::kTextAlignCenter);
// I18N: Random percentage of energy pools
_energyRandom = new GUI::CheckboxWidget(this, "MapGenerator.EnergyRandom", _("Random"), _("Picks the random amount of energy pools."), kEnergyRandCmd);
// I18N: Percentage of terrain
_terrainDesc = new GUI::StaticTextWidget(this, "MapGenerator.Terrain", _("Terrain"));
_terrainDesc->setAlign(Graphics::kTextAlignCenter);
_terrainSlider = new GUI::SliderWidget(this, "MapGenerator.TerrainSlider", Common::U32String(), kTerrainCmd);
_terrainSlider->setMinValue(0); _terrainSlider->setMaxValue(6);
// I18N: Terrain slider label
_terrainLabel = new GUI::StaticTextWidget(this, "MapGenerator.TerrainSliderLabel", _("Barren - Rough"), Common::U32String());
_terrainLabel->setAlign(Graphics::kTextAlignCenter);
// I18N: Random percentage of terrain
_terrainRandom = new GUI::CheckboxWidget(this, "MapGenerator.TerrainRandom", _("Random"), _("Picks the random amount of terrain level."), kTerrainRandCmd);
// I18N: Percentage of water
_waterDesc = new GUI::StaticTextWidget(this, "MapGenerator.Water", _("Water"));
_waterDesc->setAlign(Graphics::kTextAlignCenter);
_waterSlider = new GUI::SliderWidget(this, "MapGenerator.WaterSlider", Common::U32String(), kWaterCmd);
_waterSlider->setMinValue(0); _waterSlider->setMaxValue(6);
// I18N: Water slider label
_waterLabel = new GUI::StaticTextWidget(this, "MapGenerator.WaterSliderLabel", _("Driest - Wettest"), Common::U32String());
_waterLabel->setAlign(Graphics::kTextAlignCenter);
// I18N: Random percentage of water
_waterRandom = new GUI::CheckboxWidget(this, "MapGenerator.WaterRandom", _("Random"), _("Picks the random amount of water."), kWaterRandCmd);
_cancelButton = new GUI::ButtonWidget(this, "MapGenerator.Cancel", _("Cancel"), Common::U32String(), kCancelCmd);
// I18N: Generate new map
_generateButton = new GUI::ButtonWidget(this, "MapGenerator.Generate", _("Generate"), Common::U32String(), kGenerateCmd);
refresh();
}
void MapGeneratorDialog::refresh() {
_refreshing = true;
// ALGORITHM
bool randomAlgorithm = true;
if (ConfMan.hasKey("map_algorithm"))
randomAlgorithm = ConfMan.getInt("map_algorithm") == 0;
_algorGroup->setEnabled(!randomAlgorithm);
_algorRandom->setState(randomAlgorithm);
if (!randomAlgorithm)
_algorGroup->setValue(ConfMan.getInt("map_algorithm"));
// SIZE
bool randomSize = true;
if (ConfMan.hasKey("map_size"))
randomSize = ConfMan.getInt("map_size") == 0;
_sizeGroup->setEnabled(!randomSize);
_sizeRandom->setState(randomSize);
if (!randomSize)
_sizeGroup->setValue(ConfMan.getInt("map_size"));
// TILESET
bool randomTileset = true;
if (ConfMan.hasKey("map_tileset"))
randomTileset = ConfMan.getInt("map_tileset") == 0;
_tileGroup->setEnabled(!randomTileset);
_tileRandom->setState(randomTileset);
if (!randomTileset)
_tileGroup->setValue(ConfMan.getInt("map_tileset"));
// ENERGY
bool randomEnergy = true;
if (ConfMan.hasKey("map_energy"))
randomEnergy = ConfMan.getInt("map_energy") == -1;
_energySlider->setEnabled(!randomEnergy);
_energyRandom->setState(randomEnergy);
if (!randomEnergy)
_energySlider->setValue(ConfMan.getInt("map_energy"));
else if (ConfMan.hasKey("prev_map_energy"))
_energySlider->setValue(ConfMan.getInt("prev_map_energy"));
else
_energySlider->setValue(3);
// TERRAIN
bool randomTerrain = true;
if (ConfMan.hasKey("map_terrain"))
randomTerrain = ConfMan.getInt("map_terrain") == -1;
_terrainSlider->setEnabled(!randomTerrain);
_terrainRandom->setState(randomTerrain);
if (!randomTerrain)
_terrainSlider->setValue(ConfMan.getInt("map_terrain"));
else if (ConfMan.hasKey("prev_map_terrain"))
_terrainSlider->setValue(ConfMan.getInt("prev_map_terrain"));
else
_terrainSlider->setValue(3);
// WATER
bool randomWater = true;
if (ConfMan.hasKey("map_water"))
randomWater = ConfMan.getInt("map_water") == -1;
_waterSlider->setEnabled(!randomWater);
_waterRandom->setState(randomWater);
if (!randomWater)
_waterSlider->setValue(ConfMan.getInt("map_water"));
else if (ConfMan.hasKey("prev_map_water"))
_waterSlider->setValue(ConfMan.getInt("prev_map_water"));
else
_waterSlider->setValue(3);
drawDialog(GUI::kDrawLayerForeground);
_refreshing = false;
}
void MapGeneratorDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
if (_refreshing)
return;
switch(cmd) {
case kAlgorCmd:
ConfMan.setInt("map_algorithm", data);
break;
case kAlgorRandCmd:
if (data == 1) {
if (ConfMan.hasKey("map_algorithm") && ConfMan.getInt("map_algorithm") != 0)
// Store previous value
ConfMan.setInt("prev_map_algorithm", ConfMan.getInt("map_algorithm"));
ConfMan.setInt("map_algorithm", 0);
} else {
// Restore previous value
int previousValue = SPIFF_GEN;
if (ConfMan.hasKey("prev_map_algorithm"))
previousValue = ConfMan.getInt("prev_map_algorithm");
ConfMan.setInt("map_algorithm", previousValue);
}
refresh();
break;
case kSizeCmd:
ConfMan.setInt("map_size", data);
break;
case kSizeRandCmd:
if (data == 1) {
if (ConfMan.hasKey("map_size") && ConfMan.getInt("map_size") != 0)
// Store previous value
ConfMan.setInt("prev_map_size", ConfMan.getInt("map_size"));
ConfMan.setInt("map_size", 0);
} else {
// Restore previous value
int previousValue = 4;
if (ConfMan.hasKey("prev_map_size"))
previousValue = ConfMan.getInt("prev_map_size");
ConfMan.setInt("map_size", previousValue);
}
refresh();
break;
case kTileCmd:
ConfMan.setInt("map_tileset", data);
break;
case kTileRandCmd:
if (data == 1) {
if (ConfMan.hasKey("map_tileset") && ConfMan.getInt("map_tileset") != 0)
// Store previous value
ConfMan.setInt("prev_map_tileset", ConfMan.getInt("map_tileset"));
ConfMan.setInt("map_tileset", 0);
} else {
// Restore previous value
int previousValue = 5;
if (ConfMan.hasKey("prev_map_tileset"))
previousValue = ConfMan.getInt("prev_map_tileset");
ConfMan.setInt("map_tileset", previousValue);
}
refresh();
break;
case kEnergyCmd:
ConfMan.setInt("map_energy", data);
break;
case kEnergyRandCmd:
if (data == 1) {
if (ConfMan.hasKey("map_energy") && ConfMan.getInt("map_energy") != -1)
// Store previous value
ConfMan.setInt("prev_map_energy", ConfMan.getInt("map_energy"));
ConfMan.setInt("map_energy", -1);
} else {
// Restore previous value
int previousValue = 3;
if (ConfMan.hasKey("prev_map_energy"))
previousValue = ConfMan.getInt("prev_map_energy");
ConfMan.setInt("map_energy", previousValue);
}
refresh();
break;
case kTerrainCmd:
ConfMan.setInt("map_terrain", data);
break;
case kTerrainRandCmd:
if (data == 1) {
if (ConfMan.hasKey("map_terrain") && ConfMan.getInt("map_terrain") != -1)
// Store previous value
ConfMan.setInt("prev_map_terrain", ConfMan.getInt("map_terrain"));
ConfMan.setInt("map_terrain", -1);
} else {
// Restore previous value
int previousValue = 3;
if (ConfMan.hasKey("prev_map_terrain"))
previousValue = ConfMan.getInt("prev_map_terrain");
ConfMan.setInt("map_terrain", previousValue);
}
refresh();
break;
case kWaterCmd:
ConfMan.setInt("map_water", data);
break;
case kWaterRandCmd:
if (data == 1) {
if (ConfMan.hasKey("map_water") && ConfMan.getInt("map_water") != -1)
// Store previous value
ConfMan.setInt("prev_map_water", ConfMan.getInt("map_water"));
ConfMan.setInt("map_water", -1);
} else {
// Restore previous value
int previousValue = 3;
if (ConfMan.hasKey("prev_map_water"))
previousValue = ConfMan.getInt("prev_map_water");
ConfMan.setInt("map_water", previousValue);
}
refresh();
break;
case kCancelCmd:
setResult(0);
close();
break;
case kGenerateCmd:
ConfMan.flushToDisk();
setResult(1);
close();
break;
default:
Dialog::handleCommand(sender, cmd, data);
}
}
void MapGeneratorDialog::handleKeyDown(Common::KeyState state) {
switch (state.keycode) {
case Common::KEYCODE_RETURN:
case Common::KEYCODE_KP_ENTER:
ConfMan.flushToDisk();
setResult(1);
close();
break;
default:
Dialog::handleKeyDown(state);
}
}
} // End of namespace Scumm

View File

@@ -0,0 +1,90 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_DIALOG_MAP_GENERATOR_H
#define SCUMM_DIALOG_MAP_GENERATOR_H
#include "gui/dialog.h"
#include "gui/widget.h"
namespace Scumm {
class MapGeneratorDialog : public Dialog {
public:
MapGeneratorDialog(bool demo);
void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
void handleKeyDown(Common::KeyState state) override;
private:
void refresh();
bool _refreshing;
GUI::StaticTextWidget *_dialogTitle;
GUI::StaticTextWidget *_algorDesc;
GUI::RadiobuttonGroup *_algorGroup;
GUI::RadiobuttonWidget *_algorSpiff;
GUI::CheckboxWidget *_algorRandom;
GUI::StaticTextWidget *_sizeDesc;
GUI::RadiobuttonGroup *_sizeGroup;
GUI::RadiobuttonWidget *_sizeSmall;
GUI::RadiobuttonWidget *_sizeMedium;
GUI::RadiobuttonWidget *_sizeLarge;
GUI::RadiobuttonWidget *_sizeHuge;
GUI::RadiobuttonWidget *_sizeSAI;
GUI::RadiobuttonWidget *_sizeRidiculous;
GUI::RadiobuttonWidget *_sizeMax;
GUI::CheckboxWidget *_sizeRandom;
GUI::StaticTextWidget *_tileDesc;
GUI::RadiobuttonGroup *_tileGroup;
GUI::RadiobuttonWidget *_tileAblation;
GUI::RadiobuttonWidget *_tileEmerau;
GUI::RadiobuttonWidget *_tileKyanite;
GUI::RadiobuttonWidget *_tileDrijim;
GUI::RadiobuttonWidget *_tileZanateros;
GUI::RadiobuttonWidget *_tileTerrandra;
GUI::CheckboxWidget *_tileRandom;
GUI::StaticTextWidget *_energyDesc;
GUI::SliderWidget *_energySlider;
GUI::StaticTextWidget *_energyLabel;
GUI::CheckboxWidget *_energyRandom;
GUI::StaticTextWidget *_terrainDesc;
GUI::SliderWidget *_terrainSlider;
GUI::StaticTextWidget *_terrainLabel;
GUI::CheckboxWidget *_terrainRandom;
GUI::StaticTextWidget *_waterDesc;
GUI::SliderWidget *_waterSlider;
GUI::StaticTextWidget *_waterLabel;
GUI::CheckboxWidget *_waterRandom;
GUI::ButtonWidget *_cancelButton;
GUI::ButtonWidget *_generateButton;
};
} // End of namespace Scumm
#endif

View File

@@ -0,0 +1,393 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_MAP_DATA_H
#define SCUMM_HE_MOONBASE_MAP_DATA_H
// This header contains file data used by the map generator, mostly
// the Template.thm and Template.wiz data taken from the Moonbase Console
// source repository. Gzipped manually and then converted using
// https://notisrac.github.io/FileToCArray/ (https://github.com/notisrac/FileToCArray)
// Template.thm.gz (Map Icon).
// (https://github.com/michaelbarlow7/moonbase-console/blob/master/MoonbaseConsole/Template.thm)
#define TEMPLATE_THM_SIZE 4296 // The actual non-compressed size of Template.thm.
const byte Template_thm[] = {
0x1f, 0x8b, 0x08, 0x08, 0x5f, 0x3e, 0x4b, 0x65, 0x00, 0x03, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61,
0x74, 0x65, 0x2e, 0x74, 0x68, 0x6d, 0x00, 0xad, 0x97, 0x0f, 0x7c, 0x13, 0xd5, 0x1d, 0xc0, 0x7f,
0x97, 0x5c, 0xe1, 0xaa, 0x87, 0x1c, 0x5b, 0x26, 0x99, 0x2b, 0xa3, 0x2d, 0x4d, 0xd7, 0xac, 0x89,
0x23, 0xdb, 0x3a, 0x44, 0x40, 0xe8, 0x41, 0x5b, 0xe8, 0x54, 0x58, 0x90, 0x16, 0x3b, 0xd0, 0x6e,
0xa9, 0xb6, 0x25, 0xb2, 0xb9, 0x76, 0x0e, 0x59, 0x27, 0x64, 0x72, 0x20, 0xd0, 0x14, 0x50, 0xa2,
0x73, 0xac, 0x52, 0x98, 0x1e, 0xca, 0xd2, 0xb3, 0xab, 0x10, 0xb6, 0x39, 0x43, 0xbb, 0xd5, 0xa5,
0xfc, 0x0d, 0x0a, 0x18, 0x14, 0x24, 0xa5, 0x73, 0x7a, 0x9b, 0x28, 0x61, 0x75, 0xa5, 0xb8, 0xcd,
0x3d, 0xdf, 0xbb, 0x97, 0x4b, 0xd2, 0xd2, 0x7e, 0x3e, 0xeb, 0x3e, 0xbb, 0x5f, 0xaf, 0xef, 0xe5,
0xcf, 0xfb, 0xde, 0xef, 0xef, 0x7b, 0xbf, 0x14, 0x96, 0x2f, 0xa8, 0x00, 0x10, 0x0e, 0xe1, 0x61,
0x3e, 0x00, 0x98, 0x0c, 0xf8, 0x5f, 0x5e, 0xfc, 0x76, 0x96, 0x88, 0x8b, 0x01, 0x8c, 0x1c, 0x90,
0xcb, 0x8f, 0x05, 0xfc, 0x7e, 0xbf, 0x36, 0xf3, 0x97, 0x93, 0x69, 0x39, 0xb9, 0x10, 0x96, 0x72,
0x84, 0x90, 0x36, 0x43, 0xda, 0x0d, 0x30, 0x13, 0x60, 0x19, 0xc0, 0x23, 0x00, 0xcf, 0x00, 0x1c,
0x04, 0x88, 0x02, 0x20, 0xfb, 0x54, 0x74, 0x7b, 0x35, 0x5a, 0xd5, 0x82, 0xf6, 0x9f, 0x41, 0xbd,
0x7d, 0x48, 0xac, 0xf7, 0x8a, 0x0d, 0xb2, 0x28, 0xc9, 0xa2, 0x57, 0xc9, 0x19, 0x0b, 0x33, 0x4d,
0xb0, 0x24, 0x0b, 0x56, 0xde, 0x0c, 0x1b, 0x66, 0x40, 0xcb, 0x02, 0x68, 0xab, 0x14, 0x3a, 0x56,
0x65, 0x9e, 0xd8, 0x32, 0xb5, 0x47, 0x9e, 0x13, 0x7b, 0x65, 0x11, 0x3a, 0x5d, 0x89, 0x8e, 0x00,
0x06, 0xbb, 0xdc, 0x52, 0x2f, 0x64, 0x89, 0x00, 0xd5, 0x00, 0x4d, 0x00, 0xfb, 0x00, 0xde, 0xc2,
0x68, 0xfc, 0x97, 0x6f, 0x43, 0xe5, 0x4b, 0xd1, 0xba, 0xf5, 0xa8, 0xad, 0x1d, 0x9d, 0x8b, 0xe2,
0x6f, 0xa2, 0x08, 0x20, 0x05, 0x90, 0x04, 0x08, 0x2b, 0x32, 0x1b, 0xe0, 0x3e, 0x80, 0x46, 0x80,
0x97, 0x00, 0xde, 0x24, 0x0b, 0xec, 0x28, 0xbf, 0x02, 0x95, 0x3f, 0x86, 0xd6, 0xed, 0x47, 0x6d,
0xbd, 0xe8, 0x9c, 0x0d, 0x2e, 0xdd, 0x0d, 0xaf, 0x6e, 0x80, 0x1d, 0xfb, 0xe0, 0xc1, 0x5e, 0x28,
0x29, 0x86, 0x15, 0xcb, 0x60, 0x6d, 0x1d, 0xf8, 0x36, 0xc1, 0xde, 0x16, 0x38, 0x78, 0x00, 0x4e,
0x77, 0xe5, 0xbf, 0xfd, 0x5a, 0xf9, 0xbb, 0xe7, 0xd7, 0x7d, 0xf8, 0x7e, 0xdb, 0x95, 0xab, 0xe7,
0x10, 0xe4, 0x9d, 0x00, 0x67, 0x37, 0xd4, 0x3d, 0x0e, 0x4f, 0xb9, 0x61, 0xef, 0x2c, 0x66, 0x42,
0x9e, 0x29, 0x77, 0x66, 0xde, 0xcc, 0x45, 0x33, 0x9c, 0x55, 0x0b, 0x6b, 0x1e, 0x76, 0xad, 0x69,
0x5a, 0xf3, 0xe4, 0xee, 0x1d, 0xad, 0x81, 0x03, 0x5d, 0x87, 0x4f, 0x9e, 0x3d, 0x7f, 0xa9, 0xaf,
0x6f, 0x76, 0x2f, 0xdc, 0xfb, 0x1e, 0xac, 0xbd, 0x08, 0x2d, 0x97, 0xa1, 0xa3, 0x1f, 0x22, 0xe1,
0x70, 0x2c, 0xa6, 0x06, 0x02, 0xde, 0x50, 0x38, 0x5c, 0x2f, 0x2b, 0xc1, 0xa8, 0x2a, 0xfa, 0x94,
0x50, 0x54, 0x55, 0x22, 0x6a, 0x7d, 0xb3, 0xe2, 0xf6, 0xc9, 0x4e, 0xaf, 0xec, 0x93, 0x15, 0x39,
0x14, 0x11, 0x5d, 0xf5, 0xd8, 0x3f, 0xea, 0x00, 0xf2, 0x05, 0x42, 0xc4, 0x63, 0x92, 0xec, 0x6a,
0x0e, 0xd6, 0xcb, 0x21, 0x67, 0x83, 0xcf, 0xea, 0xf6, 0x49, 0x72, 0xb0, 0xa2, 0x5e, 0x72, 0x7a,
0x03, 0xc1, 0xe8, 0x80, 0x12, 0x56, 0xdd, 0x5e, 0x19, 0xbb, 0xd4, 0x17, 0x8c, 0x4a, 0x81, 0x68,
0x54, 0x8d, 0x45, 0x63, 0x28, 0xa2, 0x0e, 0x78, 0xe5, 0xa0, 0x1a, 0x1b, 0x08, 0x47, 0xa2, 0xa2,
0xdb, 0xe7, 0xf2, 0x06, 0x1d, 0x15, 0xf8, 0x8d, 0xa8, 0x12, 0x46, 0xe1, 0xe8, 0x80, 0x1c, 0x08,
0x87, 0xa3, 0xc8, 0xdd, 0xd0, 0xec, 0x95, 0x23, 0x4e, 0x97, 0xd4, 0x1c, 0x44, 0x8e, 0xd2, 0x86,
0x50, 0x58, 0x95, 0x9a, 0x07, 0x22, 0x51, 0x24, 0x2b, 0xe1, 0x50, 0x18, 0x49, 0xde, 0x60, 0x85,
0xab, 0x19, 0xe3, 0xbc, 0x5e, 0x5f, 0x24, 0xa2, 0x2a, 0x4a, 0x24, 0x18, 0x8c, 0x29, 0x8a, 0x2a,
0x35, 0xc8, 0xd1, 0xc8, 0x40, 0xbd, 0x4b, 0xae, 0x28, 0xf5, 0x4a, 0xee, 0xa0, 0xec, 0x0b, 0x05,
0x65, 0x35, 0xd0, 0x1c, 0x6d, 0x70, 0xc9, 0xcd, 0x0d, 0x21, 0xc9, 0x25, 0xd7, 0x3b, 0x7d, 0x5e,
0x97, 0x12, 0x0d, 0xc5, 0x22, 0x41, 0x55, 0x0d, 0xc7, 0x22, 0x81, 0x08, 0x8a, 0x0d, 0xe0, 0x64,
0x02, 0x60, 0x01, 0xd2, 0x00, 0xc6, 0x00, 0x8c, 0x05, 0xc0, 0xa9, 0x95, 0x0e, 0x70, 0x1d, 0xc0,
0xf5, 0x00, 0x3c, 0xc0, 0x38, 0x80, 0x1b, 0x00, 0xc6, 0x9f, 0xe8, 0x86, 0x3d, 0x3b, 0x61, 0xcd,
0x43, 0x30, 0x77, 0x2e, 0xf4, 0xf4, 0xf4, 0xb4, 0xb7, 0xb7, 0x4b, 0x92, 0x84, 0x53, 0xed, 0xe5,
0x73, 0x97, 0xd6, 0xc8, 0x47, 0xe7, 0xac, 0x6f, 0x9f, 0xb4, 0x7c, 0x03, 0xea, 0x7e, 0xfe, 0xea,
0xfe, 0x47, 0xfb, 0x76, 0x2f, 0xbf, 0xb8, 0xe5, 0x16, 0x94, 0x03, 0xe8, 0x36, 0x40, 0x4b, 0x01,
0xd5, 0x01, 0xf2, 0x02, 0xda, 0x03, 0xa8, 0x13, 0xd0, 0x59, 0xd8, 0x5d, 0x58, 0xb8, 0x7a, 0xf2,
0xe4, 0x0f, 0x4e, 0xad, 0xe8, 0x6e, 0xc9, 0xdf, 0xe8, 0x4a, 0xbf, 0x1c, 0xea, 0x3b, 0xf3, 0xec,
0x99, 0x17, 0x5c, 0x3b, 0x71, 0x36, 0x77, 0x75, 0x75, 0xed, 0x5b, 0x9c, 0xff, 0x8e, 0x7b, 0xda,
0x99, 0xb6, 0x06, 0x74, 0x6a, 0x7b, 0x47, 0x47, 0xc7, 0xae, 0x5d, 0xcf, 0x79, 0x3c, 0x1e, 0x9c,
0x4b, 0x38, 0x3d, 0x90, 0x36, 0x20, 0x6d, 0x86, 0xaf, 0xc5, 0x8b, 0x16, 0xde, 0x85, 0xdf, 0x10,
0x20, 0x7e, 0x39, 0xef, 0x28, 0x5c, 0x04, 0xc0, 0xf0, 0xda, 0x0b, 0xc6, 0x60, 0x64, 0xd3, 0xc6,
0x8c, 0xe5, 0xd2, 0xaf, 0xbb, 0x9e, 0x1f, 0x77, 0xc3, 0x78, 0x61, 0xc2, 0x67, 0x3e, 0x6b, 0xfa,
0xdc, 0x8d, 0x13, 0xcd, 0x9f, 0xbf, 0xe9, 0x0b, 0x19, 0x93, 0xbe, 0x38, 0x39, 0x33, 0x2b, 0x7b,
0x4a, 0x8e, 0x25, 0xf7, 0x4b, 0x79, 0xd6, 0x2f, 0xe7, 0xdb, 0xec, 0x37, 0x7f, 0x65, 0xaa, 0xe3,
0xab, 0x5f, 0xfb, 0x7a, 0xc1, 0x37, 0xa6, 0xdd, 0x32, 0xfd, 0xd6, 0x19, 0x33, 0x67, 0xdd, 0x36,
0x7b, 0x4e, 0xa1, 0x38, 0x77, 0x5e, 0x51, 0x71, 0xc9, 0xfc, 0x05, 0xa5, 0xdf, 0xbc, 0xfd, 0x8e,
0x3b, 0x17, 0x2e, 0xfa, 0x96, 0x73, 0xf1, 0x5d, 0x4b, 0xca, 0xca, 0x97, 0xde, 0x5d, 0xf1, 0xed,
0x65, 0xcb, 0xef, 0xb9, 0xb7, 0xf2, 0x3b, 0xdf, 0x75, 0x55, 0xdd, 0x77, 0x7f, 0x75, 0x4d, 0xed,
0x0a, 0xf7, 0x03, 0x2b, 0xbf, 0xf7, 0xfd, 0x07, 0x7f, 0x50, 0x57, 0xff, 0xc3, 0x87, 0x7e, 0xb4,
0xea, 0xe1, 0xd5, 0x3f, 0x6e, 0xf8, 0xc9, 0x23, 0x6b, 0xd6, 0x7a, 0x7e, 0xfa, 0xe8, 0x3a, 0x69,
0xfd, 0x86, 0xc7, 0x36, 0x6e, 0xda, 0xdc, 0xe8, 0x6d, 0xda, 0xb2, 0x75, 0xdb, 0xe3, 0x4f, 0x6c,
0xf7, 0x3d, 0xf9, 0xd4, 0xcf, 0x9e, 0xfe, 0xf9, 0x8e, 0x5f, 0x34, 0x3f, 0xb3, 0xb3, 0x65, 0xd7,
0xee, 0x5f, 0x3e, 0xfb, 0x9c, 0xbc, 0xe7, 0xf9, 0x17, 0xf6, 0xfe, 0xca, 0xdf, 0xaa, 0xbc, 0xd8,
0xf6, 0xeb, 0xf6, 0x97, 0xf6, 0xed, 0x0f, 0x1c, 0xf8, 0xcd, 0x6f, 0x7f, 0xf7, 0xf2, 0xef, 0x5f,
0x09, 0x1e, 0xec, 0xe8, 0xfc, 0xc3, 0x1f, 0xbb, 0x5e, 0xfd, 0x53, 0xa8, 0xfb, 0xd0, 0xe1, 0x23,
0x47, 0x8f, 0x1d, 0x0f, 0x9f, 0x78, 0xed, 0xf5, 0x93, 0xa7, 0x4e, 0xbf, 0x11, 0x39, 0xf3, 0xe6,
0x5b, 0x67, 0xcf, 0xbd, 0x7d, 0x3e, 0xda, 0x73, 0xa1, 0xf7, 0xcf, 0xef, 0xfc, 0xe5, 0xdd, 0xf7,
0xd4, 0xbf, 0xfe, 0xed, 0xfd, 0x8b, 0x1f, 0x7c, 0x78, 0x29, 0x76, 0xf9, 0xef, 0x7d, 0x1f, 0xfd,
0xa3, 0xff, 0xca, 0xc0, 0xd5, 0x8f, 0xff, 0xf9, 0xaf, 0x7f, 0x7f, 0xf2, 0x1f, 0x84, 0xf7, 0x8d,
0x79, 0x38, 0x76, 0x1b, 0x8f, 0x0b, 0xa3, 0x17, 0x45, 0x68, 0xe5, 0x1b, 0xf9, 0xcd, 0xdc, 0x26,
0xae, 0x96, 0xab, 0xe1, 0xe6, 0xb3, 0xd5, 0x44, 0xb8, 0x12, 0xb6, 0x04, 0xbf, 0x1a, 0x3d, 0xad,
0x15, 0x4b, 0x23, 0x4f, 0x48, 0x44, 0x4a, 0xd8, 0x62, 0x4d, 0x72, 0xa1, 0x08, 0xc8, 0x38, 0x6a,
0xcd, 0x12, 0x34, 0x4d, 0x23, 0xcc, 0x29, 0x86, 0x22, 0xd6, 0x02, 0x39, 0x78, 0x8f, 0x24, 0xf2,
0xbf, 0xe8, 0xb6, 0x49, 0xa3, 0x61, 0x6d, 0xc0, 0x12, 0x17, 0x30, 0x19, 0x71, 0x49, 0xe4, 0x80,
0x65, 0x54, 0xfa, 0x11, 0x9a, 0x1f, 0xfb, 0x8d, 0xd8, 0x49, 0x6c, 0xa3, 0xac, 0x1c, 0x52, 0x59,
0x1a, 0x8f, 0xc5, 0xaf, 0x46, 0x5e, 0x7d, 0x0c, 0xaf, 0x2e, 0x62, 0xb3, 0x81, 0x8a, 0x41, 0x13,
0x06, 0xdf, 0x39, 0x40, 0x74, 0xcb, 0xc5, 0x6b, 0x59, 0x4d, 0x08, 0x89, 0xf2, 0x88, 0x86, 0x23,
0xf3, 0xfc, 0xbc, 0x46, 0x62, 0xab, 0xf8, 0xc3, 0x66, 0x65, 0xba, 0x52, 0xe0, 0x75, 0x48, 0x82,
0xc8, 0x31, 0x5a, 0x71, 0xe8, 0x7a, 0x11, 0x06, 0x79, 0x8e, 0xce, 0x63, 0x47, 0xe0, 0xf9, 0x85,
0x1a, 0x8d, 0xe6, 0xb6, 0x07, 0xca, 0xde, 0xa8, 0xf0, 0x3a, 0x8e, 0x99, 0xfb, 0x4c, 0x47, 0xcd,
0x5e, 0x87, 0xa3, 0x34, 0x3c, 0x2b, 0x53, 0x43, 0x1a, 0xe3, 0x2c, 0x1a, 0x85, 0xa4, 0x0c, 0x47,
0xd3, 0xad, 0x94, 0x4d, 0x81, 0x32, 0x9f, 0xd3, 0x6d, 0x7f, 0x5a, 0xf8, 0x24, 0x63, 0x86, 0xf5,
0xe3, 0xa2, 0xad, 0x7c, 0x08, 0x33, 0x33, 0x99, 0x4c, 0xe6, 0x02, 0xb3, 0x8b, 0xe9, 0xd4, 0xb8,
0x8c, 0x26, 0x60, 0xa2, 0xa3, 0x61, 0x18, 0xfd, 0x88, 0x9d, 0x55, 0xbc, 0xa3, 0xd8, 0x59, 0x2a,
0x9b, 0xc6, 0x8b, 0x81, 0xb2, 0x40, 0xd9, 0x92, 0xe2, 0xab, 0x85, 0xab, 0x2d, 0x75, 0xf6, 0xad,
0x0e, 0xa5, 0x80, 0xd0, 0x3a, 0x19, 0x11, 0x87, 0xc0, 0x63, 0xf0, 0x18, 0x28, 0x91, 0x9c, 0xa7,
0x74, 0x36, 0x94, 0x77, 0x4c, 0x98, 0xa7, 0xe9, 0xa6, 0x66, 0x6c, 0x73, 0xc8, 0xa6, 0x90, 0x29,
0x64, 0x56, 0x33, 0x94, 0x02, 0x41, 0x9c, 0x5a, 0x1c, 0x2b, 0x0c, 0xcf, 0x72, 0xdb, 0x09, 0x2d,
0x53, 0xd3, 0xad, 0x93, 0x21, 0x3c, 0x4a, 0xd4, 0x79, 0x84, 0x48, 0x39, 0x35, 0xfc, 0x94, 0x44,
0x24, 0xe7, 0xb2, 0xc2, 0x84, 0x89, 0x69, 0x21, 0x33, 0xe5, 0x11, 0xa2, 0x8a, 0xe7, 0x92, 0x20,
0x09, 0x2e, 0xde, 0xc5, 0x89, 0x5c, 0x16, 0xeb, 0x61, 0x2a, 0x19, 0x12, 0x59, 0x3c, 0xd3, 0x79,
0x36, 0x3a, 0x50, 0xfd, 0x36, 0xf3, 0x34, 0x8e, 0xaa, 0xd9, 0x6d, 0x77, 0xdb, 0xb9, 0x2c, 0xab,
0xc5, 0x6a, 0x71, 0xf1, 0x6e, 0x3b, 0x25, 0xca, 0xa6, 0xf5, 0x82, 0xc4, 0x8b, 0x1a, 0x29, 0x8b,
0xcd, 0x64, 0x3c, 0x8c, 0x8b, 0xa7, 0x1c, 0x03, 0xb5, 0xd9, 0x96, 0x2a, 0x84, 0x47, 0xb4, 0x12,
0xc4, 0xf0, 0xf4, 0x40, 0x99, 0xd7, 0x41, 0x68, 0xdc, 0x24, 0xa2, 0x4d, 0xa9, 0xd5, 0x6d, 0xb7,
0x5a, 0x64, 0x53, 0x15, 0xef, 0xc2, 0x34, 0x6a, 0x29, 0x11, 0x17, 0x9f, 0xf4, 0x1d, 0xc0, 0x05,
0x23, 0x15, 0x5d, 0x3f, 0xbf, 0x66, 0xa9, 0xdb, 0x2e, 0x09, 0xb2, 0x49, 0x29, 0x50, 0x33, 0x08,
0x4f, 0x35, 0x13, 0x1e, 0xb6, 0x33, 0x83, 0xda, 0x1b, 0x4a, 0xd8, 0x2b, 0x72, 0x3a, 0x2d, 0xe9,
0x3d, 0xc2, 0xd2, 0x99, 0x0c, 0x10, 0xff, 0xcb, 0xa6, 0xfd, 0x38, 0x8a, 0x91, 0x7b, 0xac, 0x96,
0x7e, 0x76, 0x62, 0xda, 0xb4, 0x71, 0x47, 0xb4, 0xd5, 0x74, 0xbd, 0x24, 0x60, 0x9a, 0x36, 0x23,
0xf6, 0x02, 0x54, 0x6a, 0x71, 0xc8, 0xc5, 0x47, 0x97, 0x11, 0x28, 0x43, 0xd7, 0x8e, 0x8e, 0x94,
0x57, 0x6a, 0x25, 0x99, 0xc1, 0x65, 0xf5, 0xb3, 0xe6, 0x74, 0x73, 0xfa, 0xc4, 0xb4, 0x27, 0xf8,
0x15, 0xdc, 0xfd, 0xd8, 0x4e, 0xca, 0xa1, 0x76, 0xe2, 0x88, 0x32, 0x84, 0x06, 0x40, 0x6b, 0xcd,
0x98, 0x42, 0xd4, 0x6d, 0x07, 0xd0, 0x72, 0x97, 0xa5, 0x31, 0xe0, 0x26, 0x9d, 0xe4, 0x08, 0xef,
0x56, 0x7c, 0xf7, 0xc3, 0x16, 0x68, 0x62, 0xa9, 0xbf, 0x08, 0x93, 0xb0, 0x01, 0x47, 0x94, 0xc4,
0xc2, 0x63, 0x48, 0xd3, 0x78, 0xa4, 0x9e, 0x53, 0xbd, 0x87, 0x47, 0x5b, 0xab, 0x96, 0x71, 0x3a,
0xcf, 0x6a, 0x21, 0xf6, 0xde, 0x94, 0x6e, 0x4e, 0xeb, 0x87, 0x53, 0xd0, 0x8e, 0x99, 0x84, 0xa8,
0x65, 0x0a, 0x4f, 0xbd, 0x27, 0xf2, 0x94, 0xa8, 0xd7, 0x46, 0x32, 0x22, 0x94, 0x7b, 0x5c, 0xa8,
0xe6, 0xb2, 0x21, 0x64, 0x26, 0x3c, 0x4c, 0xcc, 0xba, 0xc2, 0x4d, 0x1b, 0x77, 0x63, 0xfa, 0x15,
0xf6, 0x14, 0xee, 0xb1, 0xb6, 0x11, 0x1d, 0x71, 0x07, 0x49, 0xb2, 0x97, 0xe8, 0x98, 0x14, 0xdd,
0x46, 0x06, 0x92, 0xd6, 0xd2, 0x19, 0xa9, 0x09, 0x9c, 0xc3, 0x1c, 0xd1, 0x30, 0x3c, 0x9d, 0x64,
0x9f, 0x30, 0xe1, 0x23, 0xee, 0x75, 0x96, 0xe8, 0xb6, 0x12, 0xcb, 0x03, 0xe0, 0xc6, 0xc4, 0x3c,
0x2d, 0xa6, 0x24, 0xff, 0x00, 0x52, 0x57, 0x27, 0xa3, 0x91, 0xe4, 0x1d, 0x17, 0xa6, 0x68, 0x16,
0x73, 0x59, 0xa4, 0x56, 0x95, 0x02, 0x49, 0x38, 0x64, 0x7a, 0x91, 0x6d, 0xd2, 0x48, 0xa5, 0xb0,
0x00, 0xac, 0x1a, 0x2f, 0x2f, 0x25, 0xe7, 0x92, 0xeb, 0x87, 0xd2, 0x28, 0x8f, 0x58, 0x9c, 0xcd,
0xd6, 0xdf, 0xe9, 0x73, 0x6e, 0x77, 0xe6, 0x65, 0xa9, 0x19, 0x92, 0x40, 0xbc, 0xb6, 0x91, 0x75,
0x63, 0x5a, 0x1e, 0x6e, 0x67, 0xc6, 0x40, 0x9a, 0xb6, 0x73, 0x5e, 0x9b, 0xc7, 0x43, 0xac, 0x8d,
0xd7, 0x07, 0xa9, 0x90, 0x2a, 0x3e, 0x3c, 0xdd, 0xe7, 0xc4, 0xf5, 0x80, 0x7d, 0x2f, 0x09, 0x56,
0x8b, 0x07, 0x57, 0xfd, 0x2e, 0x9c, 0x21, 0x1e, 0x43, 0x76, 0x7c, 0x67, 0x1e, 0xea, 0xfd, 0x54,
0xad, 0x74, 0xbb, 0xf5, 0xfd, 0x80, 0xac, 0xe1, 0xb0, 0x66, 0xa4, 0xde, 0x48, 0x8d, 0x90, 0xda,
0xf0, 0x30, 0xc9, 0x3a, 0xa0, 0x32, 0xd8, 0xbe, 0xc1, 0xf5, 0xa6, 0xc7, 0x87, 0xf2, 0xaa, 0x39,
0x03, 0x90, 0xda, 0x22, 0x75, 0x95, 0xc9, 0xe8, 0x34, 0x80, 0xe1, 0xd6, 0x0e, 0xfb, 0x4e, 0x7c,
0x7f, 0xa1, 0x3c, 0x05, 0xef, 0xef, 0x0c, 0x88, 0x6c, 0xc8, 0x4c, 0x33, 0x8d, 0xec, 0x21, 0x43,
0x59, 0xa9, 0x84, 0xc1, 0x5a, 0xa5, 0xf2, 0x0c, 0xa0, 0xc4, 0x4f, 0x41, 0xe2, 0x1d, 0xa2, 0x1d,
0xdd, 0xe3, 0x46, 0xe6, 0x5d, 0xab, 0x25, 0x8d, 0x83, 0x2e, 0xf1, 0x33, 0x95, 0x2f, 0xc2, 0xe1,
0xd3, 0x6b, 0x0b, 0xf3, 0xae, 0xb1, 0x75, 0xd0, 0xfa, 0x6b, 0xf4, 0xd5, 0xf5, 0xc3, 0x3f, 0x92,
0x78, 0x3f, 0xe9, 0x6d, 0xf8, 0x12, 0xed, 0xd7, 0x13, 0xd9, 0xcb, 0x45, 0xae, 0x30, 0xb1, 0xef,
0x0e, 0x17, 0x85, 0xa1, 0x3c, 0x6d, 0x48, 0xf0, 0x1a, 0xb5, 0x4e, 0xa9, 0x96, 0xf4, 0x35, 0xf4,
0x64, 0x81, 0xc1, 0x31, 0x4d, 0xb5, 0x51, 0xaf, 0x88, 0x61, 0x2c, 0x4f, 0xf0, 0x36, 0x63, 0xdd,
0x6a, 0x71, 0xcf, 0x45, 0xba, 0x07, 0x16, 0x0c, 0x29, 0xc4, 0x21, 0x66, 0xda, 0x92, 0xb1, 0x18,
0xba, 0x2b, 0x0f, 0xe6, 0xd5, 0x6a, 0xdd, 0x88, 0x05, 0xf3, 0x8c, 0xd7, 0x9c, 0xce, 0x83, 0xcf,
0x06, 0xb0, 0xa5, 0xc4, 0xd5, 0x36, 0xbc, 0x6c, 0x8a, 0xf7, 0x70, 0x74, 0x7f, 0x64, 0xb4, 0x4a,
0xd0, 0x4f, 0xbf, 0xd4, 0xe7, 0xfe, 0x77, 0x33, 0x03, 0xd0, 0x5e, 0x89, 0x74, 0x23, 0xc6, 0x44,
0xcf, 0x33, 0x3c, 0x8f, 0x19, 0x91, 0x02, 0x29, 0xf9, 0x4c, 0x78, 0xb9, 0x1a, 0x8d, 0xfc, 0x66,
0x8e, 0x57, 0xe9, 0x28, 0xb5, 0x1a, 0xcc, 0x2b, 0x61, 0x8b, 0x12, 0x3c, 0xc3, 0xff, 0x81, 0x47,
0xba, 0xb9, 0x91, 0x23, 0x30, 0x5a, 0xa9, 0x4d, 0xf4, 0x9a, 0x60, 0xcb, 0x89, 0x77, 0x61, 0x60,
0x33, 0xc4, 0x7d, 0x49, 0x9e, 0xab, 0xfb, 0x80, 0x19, 0xd1, 0x1b, 0x4c, 0xe2, 0x7b, 0x46, 0xd0,
0x7b, 0xe1, 0x5c, 0x96, 0xf0, 0x68, 0x8f, 0x08, 0x36, 0x63, 0x82, 0x0c, 0x09, 0x1f, 0xd0, 0x7e,
0xcc, 0x30, 0x24, 0x32, 0xc9, 0xa7, 0x91, 0xcf, 0x8c, 0xf0, 0x29, 0xb1, 0xab, 0x44, 0x05, 0xc8,
0x10, 0x00, 0x00
};
// Template.wiz.gz (Map Thumbnail; modified in-code to display stats).
// (https://github.com/michaelbarlow7/moonbase-console/blob/master/MoonbaseConsole/Template.wiz)
#define TEMPLATE_WIZ_SIZE 39738 // The actual non-compressed size of Template.wiz.
const byte Template_wiz[] = {
0x1f, 0x8b, 0x08, 0x08, 0xf4, 0x21, 0x4c, 0x65, 0x00, 0x03, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61,
0x74, 0x65, 0x2e, 0x77, 0x69, 0x7a, 0x00, 0xed, 0x5d, 0x0d, 0x9c, 0x1b, 0x45, 0x15, 0x7f, 0x48,
0x80, 0x05, 0x0e, 0x08, 0x7a, 0x6a, 0xda, 0x5e, 0x81, 0x53, 0xee, 0x2e, 0x69, 0x0b, 0xb4, 0x16,
0x84, 0x42, 0xf9, 0xe8, 0x81, 0x05, 0x22, 0x1c, 0xed, 0x1e, 0xf4, 0x20, 0x82, 0x20, 0x9b, 0x0a,
0x12, 0x51, 0xb8, 0x88, 0x80, 0x91, 0xb6, 0x72, 0x4b, 0x01, 0x09, 0x28, 0x12, 0x50, 0xe1, 0xa0,
0x54, 0x08, 0x1f, 0x96, 0x58, 0x4b, 0x2f, 0xa8, 0x48, 0x28, 0x5a, 0x4c, 0x05, 0x34, 0x45, 0xc0,
0x14, 0x28, 0xa4, 0x56, 0xa4, 0x8b, 0x7c, 0xf4, 0xb0, 0x4a, 0x83, 0xf8, 0x31, 0xee, 0x64, 0x32,
0xd9, 0xd9, 0xdd, 0xd9, 0x7c, 0xb4, 0xb9, 0x66, 0xfd, 0xed, 0xe6, 0xdf, 0x6b, 0x76, 0xdf, 0xbe,
0x9d, 0xbc, 0xfd, 0xef, 0xdb, 0x99, 0x79, 0x33, 0xbb, 0x6f, 0x67, 0x0d, 0x9c, 0x1c, 0x02, 0xb8,
0xe3, 0x48, 0xf5, 0xeb, 0x24, 0x00, 0x68, 0xff, 0x90, 0xfa, 0xdf, 0x0d, 0xe5, 0x3f, 0xf1, 0xc4,
0xde, 0x7e, 0x80, 0x9d, 0x05, 0xc0, 0x9f, 0x65, 0x2a, 0x60, 0xd9, 0xb2, 0x65, 0xa5, 0xa5, 0x65,
0x03, 0x78, 0x71, 0x00, 0x7f, 0x90, 0x8a, 0x01, 0x84, 0x50, 0x69, 0x09, 0x95, 0xfe, 0x00, 0x66,
0x02, 0x9c, 0x0d, 0x70, 0x15, 0xc0, 0x9d, 0x00, 0x8f, 0x03, 0x14, 0x00, 0xd0, 0x41, 0x53, 0xd1,
0x29, 0x17, 0xa0, 0xcb, 0x97, 0xa0, 0x91, 0x75, 0x68, 0xe3, 0x16, 0xd4, 0x1b, 0x8d, 0xf7, 0xc6,
0x92, 0xbd, 0x72, 0xb2, 0x37, 0x9e, 0x3a, 0x70, 0x37, 0x98, 0xd9, 0x0e, 0x67, 0x74, 0xc2, 0xc5,
0x07, 0xc3, 0xe2, 0xa3, 0x60, 0xc9, 0xc9, 0xb0, 0xfc, 0x3c, 0xef, 0xaa, 0xcb, 0x0f, 0x58, 0x7b,
0xd3, 0xd4, 0x0d, 0xc9, 0xe3, 0x46, 0x1f, 0x9b, 0x83, 0x5e, 0x38, 0x0f, 0x3d, 0x0d, 0x6a, 0xc1,
0x52, 0x44, 0xde, 0x08, 0x9d, 0xbd, 0x00, 0x17, 0x00, 0xdc, 0x08, 0xb0, 0x12, 0xe0, 0x25, 0xb5,
0x68, 0xf5, 0xdf, 0xe4, 0x29, 0x68, 0xe0, 0x4c, 0x34, 0x74, 0x0d, 0x5a, 0xbe, 0x02, 0xad, 0x2f,
0xa8, 0x9a, 0x28, 0x0f, 0x28, 0x05, 0x48, 0x06, 0xa4, 0x1a, 0x72, 0x2c, 0xc0, 0xfc, 0xd2, 0x21,
0x3d, 0x0c, 0xf0, 0x22, 0xde, 0xe1, 0x20, 0x34, 0x39, 0x84, 0x06, 0xae, 0x45, 0x43, 0x23, 0x68,
0xf9, 0x46, 0xb4, 0x7e, 0x0a, 0x6c, 0x3e, 0x0b, 0x9e, 0x5c, 0x0c, 0xb7, 0xaf, 0x84, 0x4b, 0x36,
0xc2, 0x89, 0xb3, 0xe1, 0xa2, 0xb3, 0x61, 0xe1, 0x20, 0x24, 0xae, 0x87, 0x07, 0x97, 0xc0, 0xe3,
0x8f, 0xc0, 0x0b, 0xab, 0x27, 0xbf, 0xf2, 0xec, 0xc0, 0xeb, 0xaf, 0x0e, 0xbd, 0xf3, 0xe6, 0xf2,
0xad, 0xef, 0xaf, 0x47, 0xe0, 0x5f, 0x0b, 0xe2, 0x1a, 0x18, 0xbc, 0x19, 0x6e, 0x8b, 0xc0, 0x83,
0x47, 0xef, 0xb4, 0xaf, 0xbf, 0xbd, 0x7b, 0xa6, 0x7f, 0xe6, 0x9c, 0xa3, 0xc4, 0xf0, 0x69, 0x17,
0x5e, 0x21, 0x2d, 0xb8, 0x71, 0xc1, 0xad, 0x4b, 0x6f, 0x7f, 0x28, 0xfd, 0xc8, 0xea, 0xa7, 0x9e,
0x7b, 0xf9, 0xd5, 0xcd, 0x5b, 0xb6, 0x1c, 0xbb, 0x11, 0xce, 0xdd, 0x04, 0x0b, 0xdf, 0x82, 0x25,
0xef, 0xc2, 0xaa, 0xf7, 0x20, 0x9f, 0xcb, 0x8d, 0x8e, 0x2a, 0xe9, 0x74, 0x3c, 0x9b, 0xcb, 0x45,
0x93, 0xa9, 0x4c, 0x41, 0xe9, 0x4d, 0xa4, 0xb2, 0x05, 0x25, 0x95, 0x57, 0xa2, 0xc3, 0xa9, 0x48,
0x22, 0x29, 0xc6, 0x93, 0x89, 0x64, 0x2a, 0x99, 0xcd, 0xf7, 0x4a, 0x51, 0x95, 0x1f, 0xa5, 0x88,
0x12, 0xe9, 0x2c, 0x66, 0x4c, 0x4e, 0x4a, 0xc3, 0x99, 0x68, 0x32, 0x2b, 0xc6, 0x12, 0x81, 0x48,
0x42, 0x4e, 0x66, 0x42, 0x51, 0x59, 0x8c, 0xa7, 0x33, 0x85, 0x62, 0x2a, 0xa7, 0x44, 0xe2, 0x49,
0x95, 0xd2, 0x44, 0xa6, 0x20, 0xa7, 0x0b, 0x05, 0x65, 0xb4, 0x30, 0x8a, 0xf2, 0x4a, 0x31, 0x9e,
0xcc, 0x28, 0xa3, 0xc5, 0x5c, 0xbe, 0xd0, 0x1b, 0x49, 0x48, 0xf1, 0xcc, 0xb4, 0x90, 0x2a, 0x28,
0xa4, 0x72, 0x28, 0x57, 0x28, 0x26, 0xd3, 0xb9, 0x5c, 0x01, 0x45, 0x62, 0xc3, 0xf1, 0x64, 0x5e,
0x94, 0xe4, 0xe1, 0x0c, 0x9a, 0x16, 0x8c, 0x65, 0x73, 0x8a, 0x3c, 0x5c, 0xcc, 0x17, 0x50, 0x32,
0x95, 0xcb, 0xe6, 0x90, 0x1c, 0xcf, 0x84, 0xa4, 0x61, 0xb5, 0xb8, 0x78, 0x3c, 0x91, 0xcf, 0x2b,
0xa9, 0x54, 0x3e, 0x93, 0x19, 0x4d, 0xa5, 0x14, 0x39, 0x96, 0x2c, 0xe4, 0x8b, 0x51, 0x29, 0x19,
0x0a, 0xc6, 0xe5, 0x48, 0x26, 0x99, 0xc8, 0x66, 0x92, 0x4a, 0x7a, 0xb8, 0x10, 0x93, 0x92, 0xc3,
0xb1, 0xac, 0x2c, 0x25, 0xa3, 0x62, 0x22, 0x2e, 0xa5, 0x0a, 0xd9, 0xd1, 0x7c, 0x46, 0x51, 0x72,
0xa3, 0xf9, 0x74, 0x1e, 0x8d, 0x16, 0x55, 0x67, 0x02, 0xf0, 0x00, 0xec, 0x02, 0xb0, 0x2b, 0xc0,
0x6e, 0x00, 0xaa, 0x6b, 0xed, 0x0e, 0xb0, 0x07, 0xc0, 0x9e, 0x00, 0x6d, 0x00, 0x7b, 0x01, 0xec,
0x0d, 0xb0, 0xcf, 0xda, 0x35, 0x70, 0xdf, 0x5d, 0xb0, 0xe0, 0x32, 0x38, 0xfe, 0x78, 0xd8, 0xb0,
0x61, 0xc3, 0x8a, 0x15, 0x2b, 0x64, 0x59, 0x56, 0x5d, 0xed, 0xd1, 0xf5, 0x9b, 0x17, 0x24, 0x9f,
0x39, 0xee, 0x9a, 0x15, 0x13, 0xcf, 0x59, 0x8c, 0xd6, 0xdc, 0xff, 0xfe, 0xc8, 0xd5, 0x5b, 0x96,
0x9e, 0xf3, 0xd6, 0x4d, 0x47, 0xa0, 0x03, 0x01, 0x1d, 0x03, 0xe8, 0x4c, 0x40, 0x83, 0x80, 0xe2,
0x80, 0xee, 0x03, 0xf4, 0x04, 0xa0, 0x97, 0x61, 0xe9, 0xac, 0x59, 0x57, 0xee, 0xbf, 0xff, 0xdb,
0xcf, 0x5f, 0xb4, 0x66, 0xc9, 0xe4, 0xeb, 0xa4, 0xdd, 0xdf, 0xcd, 0x6e, 0x59, 0x77, 0xcf, 0xba,
0x07, 0xa4, 0xbb, 0x54, 0x6f, 0x5e, 0xbd, 0x7a, 0xf5, 0xca, 0xfe, 0xc9, 0xaf, 0x45, 0x0e, 0x5f,
0xb7, 0x3c, 0x86, 0x9e, 0xbf, 0x65, 0xd5, 0xaa, 0x55, 0x77, 0xdf, 0x7d, 0xef, 0xa2, 0x45, 0x8b,
0x54, 0x5f, 0x52, 0xdd, 0x03, 0x95, 0xbe, 0x50, 0x69, 0x49, 0xfd, 0xf4, 0xcf, 0x39, 0xed, 0x74,
0x55, 0xe0, 0x85, 0xf2, 0x47, 0x3c, 0x75, 0xd6, 0x1c, 0x80, 0x9d, 0xda, 0x4a, 0x2b, 0x3b, 0x7d,
0x68, 0x67, 0xcf, 0x2e, 0xbb, 0xee, 0x26, 0xec, 0xbe, 0xc7, 0x9e, 0x6d, 0x7b, 0xed, 0xbd, 0x8f,
0x77, 0xdf, 0x0f, 0x7f, 0xa4, 0xfd, 0xa3, 0x1f, 0xfb, 0xb8, 0x6f, 0xdc, 0xf8, 0x09, 0x1d, 0x13,
0xf7, 0xdb, 0xff, 0x80, 0xce, 0x4f, 0x7c, 0xf2, 0xc0, 0xae, 0xee, 0x1e, 0x7f, 0x60, 0xd2, 0xe4,
0x29, 0x07, 0x1d, 0x7c, 0xc8, 0xd4, 0x69, 0x9f, 0x9a, 0x7e, 0xe8, 0x61, 0x9f, 0x3e, 0xfc, 0x88,
0x19, 0x47, 0x1e, 0x35, 0xf3, 0xe8, 0x63, 0x8e, 0x3d, 0x6e, 0x56, 0xef, 0xf1, 0x27, 0x7c, 0x66,
0xf6, 0x89, 0x27, 0x9d, 0x1c, 0xfc, 0xec, 0x29, 0xa7, 0xf6, 0x9d, 0x36, 0x67, 0xae, 0xd8, 0x7f,
0xfa, 0x19, 0xf3, 0x06, 0xce, 0x3c, 0x2b, 0xf4, 0xb9, 0xb3, 0xcf, 0xf9, 0xfc, 0xb9, 0xe7, 0x7d,
0xe1, 0x7c, 0x29, 0x3c, 0xff, 0x8b, 0x17, 0x5c, 0xf8, 0xa5, 0x8b, 0x22, 0x5f, 0xbe, 0xf8, 0x2b,
0x5f, 0xbd, 0xe4, 0xd2, 0xc1, 0xe8, 0xd7, 0x2e, 0xfb, 0xfa, 0xe5, 0x57, 0x5c, 0xf9, 0x8d, 0xd8,
0x37, 0xaf, 0x5a, 0xb0, 0x70, 0xd1, 0xb7, 0xae, 0x1e, 0x92, 0xaf, 0x59, 0x7c, 0xed, 0x75, 0xd7,
0x7f, 0xfb, 0x86, 0xf8, 0x8d, 0x37, 0x7d, 0xe7, 0xbb, 0x37, 0x7f, 0xef, 0x96, 0xc4, 0xad, 0xb7,
0x7d, 0xff, 0x07, 0x3f, 0xbc, 0xfd, 0x8e, 0xe1, 0x3b, 0xef, 0x5a, 0x72, 0xf7, 0xd2, 0x1f, 0xdd,
0x73, 0x6f, 0xf2, 0xbe, 0xfb, 0x1f, 0x78, 0xf0, 0xc7, 0xcb, 0x1e, 0x4a, 0xfd, 0x64, 0xf9, 0x4f,
0x57, 0x3c, 0xbc, 0x72, 0x24, 0xfd, 0xc8, 0xcf, 0x7e, 0xfe, 0x8b, 0x47, 0x7f, 0xf9, 0x58, 0xe6,
0xf1, 0x55, 0x4f, 0xfc, 0xea, 0xd7, 0xab, 0x9f, 0xfc, 0x4d, 0x76, 0xcd, 0x6f, 0x9f, 0x7a, 0xfa,
0x99, 0xdf, 0xfd, 0x3e, 0xb7, 0xf6, 0xd9, 0x3f, 0x3c, 0xf7, 0xfc, 0x0b, 0x7f, 0xcc, 0xaf, 0x7b,
0xf1, 0xa5, 0x97, 0xd7, 0xbf, 0xf2, 0x6a, 0x61, 0xc3, 0x9f, 0x36, 0xfe, 0xf9, 0xb5, 0xbf, 0xbc,
0xbe, 0x49, 0x79, 0xe3, 0xaf, 0x6f, 0xbe, 0xf5, 0xf6, 0x3b, 0x9b, 0x47, 0xdf, 0xfd, 0xdb, 0x96,
0xbf, 0xff, 0xe3, 0xbd, 0xad, 0xc5, 0xf7, 0xff, 0xf9, 0xc1, 0xbf, 0xfe, 0xfd, 0x9f, 0xff, 0x22,
0xb5, 0xde, 0x38, 0x01, 0xe0, 0xb6, 0x0f, 0xe6, 0xb6, 0xb9, 0x70, 0x36, 0x06, 0xbd, 0x97, 0x7a,
0xe7, 0x7a, 0xcb, 0x6b, 0x5e, 0xfd, 0x5a, 0xab, 0x6d, 0x6b, 0x1d, 0x06, 0xbd, 0x18, 0x25, 0x06,
0xbc, 0x83, 0x5e, 0xfd, 0x5a, 0xab, 0x6d, 0x6b, 0x35, 0x2b, 0x98, 0x81, 0x4b, 0x2b, 0xac, 0xd0,
0xb5, 0x56, 0xdb, 0x66, 0x07, 0x56, 0x06, 0x75, 0xac, 0x0c, 0xba, 0xac, 0xb8, 0xac, 0x70, 0x59,
0x71, 0xeb, 0x15, 0x33, 0x2b, 0x73, 0xcb, 0x3c, 0xcc, 0xf5, 0xea, 0xd7, 0x5a, 0x6d, 0x9b, 0x0b,
0x7b, 0xa1, 0xe2, 0x1d, 0x95, 0x6b, 0x48, 0x5b, 0x6b, 0xb5, 0x6d, 0xad, 0x65, 0xc5, 0xad, 0x57,
0xf8, 0xac, 0x0c, 0x7a, 0xe7, 0x7a, 0x75, 0x6d, 0x90, 0xd7, 0x6d, 0x83, 0x5c, 0x56, 0xb6, 0x9d,
0x95, 0xe9, 0x5e, 0xe7, 0x20, 0x5a, 0x42, 0xbf, 0xba, 0xd4, 0x5f, 0x5e, 0xa6, 0x6b, 0xfd, 0x06,
0xcd, 0x0e, 0xf7, 0xc3, 0xf9, 0x4c, 0x70, 0xe1, 0xa2, 0x4e, 0x4c, 0x11, 0x5c, 0x98, 0x11, 0x10,
0x26, 0x8d, 0x01, 0x02, 0xdb, 0x8c, 0xb1, 0xb0, 0xa6, 0x31, 0x04, 0x4b, 0xf6, 0xd7, 0x3c, 0x42,
0x8f, 0x9f, 0x81, 0x26, 0xd5, 0xe4, 0x01, 0x0f, 0xd5, 0xeb, 0xa9, 0x40, 0x95, 0xaa, 0x65, 0xb3,
0x12, 0x2a, 0xd3, 0x4b, 0xcb, 0xb2, 0x52, 0x39, 0x64, 0x9d, 0x2d, 0xb3, 0x96, 0x35, 0xac, 0x66,
0xc0, 0x24, 0x0b, 0x70, 0xf5, 0xf8, 0x3c, 0x18, 0xcf, 0x6a, 0x75, 0x4e, 0xfc, 0x9e, 0xb0, 0x20,
0x33, 0x08, 0x0b, 0x98, 0x19, 0xbd, 0x34, 0x2c, 0xe0, 0xe3, 0x31, 0xea, 0xf1, 0x65, 0x01, 0xa1,
0xa7, 0xe6, 0xbe, 0x54, 0x6e, 0xb6, 0xc6, 0x5a, 0xb3, 0x76, 0xa9, 0xfc, 0x12, 0x2d, 0xb8, 0xaf,
0xca, 0x4b, 0x40, 0x2d, 0x39, 0x2f, 0x86, 0x24, 0x0d, 0xc5, 0xd0, 0x1b, 0xbe, 0x6e, 0xcf, 0x1b,
0xbe, 0x62, 0x48, 0x2f, 0x93, 0x3c, 0x46, 0x3d, 0xa5, 0x5d, 0xe2, 0xec, 0xdb, 0x65, 0xda, 0xd7,
0xac, 0x47, 0x75, 0x8d, 0x47, 0x61, 0xb6, 0xc6, 0xba, 0x54, 0x9e, 0x45, 0xe6, 0x12, 0xb7, 0x95,
0x15, 0x59, 0x08, 0x49, 0x68, 0x21, 0x5a, 0x18, 0x8b, 0x84, 0xa4, 0x58, 0x04, 0x2f, 0x8d, 0xf4,
0x49, 0x42, 0x31, 0x44, 0x65, 0x58, 0x9a, 0x89, 0x16, 0x43, 0xaa, 0x6c, 0x9e, 0x5e, 0x86, 0xf5,
0x58, 0xbb, 0xa8, 0xcc, 0xb8, 0xef, 0x48, 0xdf, 0x50, 0xe9, 0x37, 0x62, 0x11, 0x52, 0x3e, 0x59,
0xc2, 0x65, 0x86, 0x05, 0xbd, 0xd7, 0xfb, 0xcb, 0xd6, 0x98, 0x35, 0x25, 0x61, 0xa4, 0xaf, 0xb6,
0x45, 0xe6, 0x12, 0xad, 0xae, 0xa8, 0xfa, 0x58, 0x89, 0x45, 0x84, 0x4e, 0xc5, 0x17, 0x09, 0x60,
0x6b, 0xb4, 0xa3, 0x88, 0x04, 0x84, 0x4e, 0xa1, 0x13, 0x4b, 0x43, 0xd2, 0x90, 0xb0, 0x75, 0x1e,
0x96, 0x29, 0x3e, 0xec, 0xad, 0x8a, 0x2f, 0x16, 0x99, 0x71, 0xee, 0xf9, 0xb0, 0xc9, 0x87, 0xf7,
0x93, 0xbd, 0x54, 0xb6, 0x75, 0x1e, 0xdd, 0x57, 0xd3, 0x23, 0x32, 0x2c, 0xc1, 0x6b, 0x64, 0x1b,
0x5e, 0x0a, 0x49, 0xb2, 0xe0, 0xe7, 0xb0, 0xc2, 0xd3, 0xa4, 0xbf, 0xce, 0xb3, 0x08, 0x5b, 0x40,
0x65, 0xc6, 0x12, 0x29, 0x0b, 0x41, 0xdd, 0xda, 0xa4, 0x1a, 0x35, 0x3f, 0x65, 0x25, 0x24, 0x29,
0xbe, 0xbc, 0x48, 0x2c, 0xd9, 0xd4, 0x4e, 0x64, 0x98, 0xfb, 0xb4, 0x98, 0x16, 0x8b, 0xa1, 0x4c,
0x94, 0xda, 0x80, 0x7f, 0xb7, 0xdb, 0xd3, 0x5d, 0xda, 0xab, 0x38, 0x8f, 0xf8, 0x4a, 0x5e, 0x94,
0x84, 0x2e, 0x4f, 0x57, 0x49, 0x46, 0x59, 0x61, 0xf5, 0x88, 0x2c, 0x24, 0x49, 0x82, 0x54, 0xde,
0x46, 0x96, 0xac, 0x58, 0xe1, 0x69, 0x92, 0x5f, 0xe7, 0x5b, 0x94, 0xf5, 0x89, 0xbd, 0x69, 0x31,
0x2f, 0xce, 0x50, 0x2d, 0xb1, 0xf2, 0x15, 0x8d, 0x97, 0x60, 0x09, 0xd5, 0x59, 0xf1, 0x57, 0x58,
0x11, 0x3a, 0xe9, 0x95, 0x44, 0x59, 0x61, 0xcf, 0x37, 0xcb, 0x0a, 0xae, 0xe7, 0x58, 0x06, 0x58,
0x3d, 0xcc, 0x14, 0xbe, 0x82, 0xcc, 0xbe, 0xd2, 0x0c, 0x56, 0x2c, 0x2c, 0xf2, 0x4a, 0x35, 0xea,
0xdb, 0xa0, 0x05, 0x2b, 0x56, 0xbc, 0xd0, 0xfa, 0x2d, 0x2f, 0x62, 0x5f, 0xc1, 0xbf, 0x3f, 0x43,
0x9a, 0x36, 0x9b, 0xfa, 0x00, 0xad, 0xe9, 0xf3, 0x22, 0xf6, 0x87, 0x68, 0x1f, 0x91, 0xe1, 0x56,
0x10, 0xcb, 0xa2, 0xe5, 0x7a, 0x05, 0x9f, 0x23, 0x7c, 0x06, 0xc9, 0xd9, 0x92, 0x4a, 0xf5, 0x22,
0x2b, 0x23, 0x7a, 0xb8, 0x04, 0xcd, 0xb7, 0x88, 0xc4, 0x78, 0x66, 0x03, 0x95, 0xdf, 0x32, 0x6a,
0x6a, 0xbf, 0xce, 0xb3, 0xa8, 0xa7, 0xd2, 0x36, 0x07, 0x2d, 0x8e, 0xb3, 0x31, 0x56, 0x68, 0x8b,
0x17, 0x16, 0xba, 0x21, 0x5c, 0x61, 0xbc, 0xab, 0x2c, 0x23, 0xcc, 0x63, 0x0d, 0x56, 0x86, 0x4b,
0x63, 0x65, 0xb5, 0xdb, 0xeb, 0xae, 0x92, 0x36, 0xdd, 0x42, 0x97, 0x78, 0x67, 0xb6, 0xc7, 0x42,
0xb3, 0xba, 0x45, 0x6c, 0x8d, 0xc1, 0xe7, 0x23, 0xa8, 0x5b, 0xd7, 0x58, 0xa9, 0x56, 0xb7, 0x60,
0x96, 0xb5, 0x5e, 0x1b, 0x3e, 0x7f, 0x01, 0x5d, 0xaf, 0xa8, 0xb4, 0xb5, 0xd4, 0x37, 0xa3, 0xfd,
0xb4, 0x40, 0xc9, 0x63, 0xb4, 0xfe, 0x9a, 0x9f, 0xe9, 0xb3, 0xf1, 0x7a, 0x57, 0x01, 0xa6, 0xdf,
0x45, 0x97, 0xf8, 0x35, 0x80, 0x95, 0xa6, 0xd9, 0x22, 0xa3, 0xcc, 0x1a, 0x46, 0xb6, 0x58, 0x56,
0x9a, 0xd3, 0xdb, 0xb6, 0x4f, 0xcf, 0x7d, 0x7b, 0xd0, 0xea, 0x38, 0xcc, 0x9e, 0x68, 0x75, 0xcc,
0x6e, 0x4f, 0xb8, 0x1f, 0xde, 0xa7, 0xd5, 0x63, 0xa9, 0xf6, 0xc4, 0xb6, 0x8e, 0x97, 0x8b, 0x0d,
0xfe, 0x8e, 0xe8, 0xb5, 0xcb, 0xde, 0x63, 0x89, 0xe9, 0xde, 0xf1, 0xbe, 0xfa, 0x3d, 0x72, 0xbc,
0x4f, 0xcf, 0x7f, 0x2b, 0xf7, 0x1e, 0x5b, 0x56, 0xea, 0xb7, 0x0b, 0x7f, 0x8c, 0xc7, 0xd5, 0xba,
0xbd, 0xc7, 0x96, 0x95, 0x71, 0xbe, 0x43, 0xeb, 0xf4, 0xe0, 0x43, 0x55, 0x5d, 0xe3, 0x71, 0xb5,
0x6e, 0xef, 0xb1, 0x84, 0xd8, 0xc0, 0xf5, 0x6a, 0xd6, 0x6d, 0xe5, 0xde, 0x2e, 0x76, 0x0c, 0x5c,
0xe6, 0x79, 0x98, 0xbe, 0x43, 0xaf, 0x52, 0xfb, 0xc0, 0x38, 0xb3, 0x10, 0xf0, 0xe8, 0x59, 0xd9,
0x91, 0xed, 0x9c, 0x5d, 0x60, 0x9e, 0x59, 0xc0, 0xe3, 0x13, 0x4e, 0x67, 0xc5, 0x3c, 0xb3, 0x40,
0x46, 0xc4, 0x9c, 0xcd, 0x0a, 0x3b, 0xcf, 0x61, 0x66, 0x45, 0x54, 0x5b, 0xff, 0x09, 0xbe, 0xc3,
0xda, 0x1b, 0xeb, 0x6f, 0xdb, 0x07, 0xdb, 0xda, 0x4e, 0x84, 0xcb, 0x63, 0xcd, 0x64, 0x06, 0x01,
0x43, 0xf1, 0x75, 0x03, 0x65, 0x05, 0xd7, 0xb5, 0x13, 0x3b, 0x26, 0xf8, 0xc6, 0xff, 0x5f, 0x62,
0xdb, 0xdb, 0x89, 0x1e, 0x8f, 0x52, 0x19, 0x6b, 0xc6, 0x23, 0xcd, 0xae, 0xaf, 0x10, 0x56, 0xf0,
0xbc, 0x01, 0xf1, 0x12, 0x32, 0x27, 0x46, 0xe6, 0xdb, 0xe8, 0x76, 0x67, 0xd6, 0x2b, 0xf3, 0x05,
0x3c, 0xa3, 0x94, 0xae, 0xcc, 0x4a, 0x90, 0xb9, 0x59, 0x6d, 0xbb, 0x33, 0x59, 0xe1, 0xcd, 0xe3,
0xb3, 0xdb, 0x9d, 0xc9, 0x8a, 0xb1, 0x17, 0x17, 0x70, 0x7b, 0x71, 0x6d, 0xd4, 0x57, 0xa8, 0x87,
0xd0, 0x39, 0x29, 0xa7, 0xb3, 0x12, 0xae, 0xcc, 0x95, 0x62, 0x2f, 0x09, 0x57, 0xe6, 0x35, 0x9d,
0xcd, 0x8a, 0x5c, 0xb9, 0x63, 0x80, 0xce, 0xa1, 0x93, 0x39, 0x70, 0xb2, 0xd5, 0xa9, 0x2d, 0x33,
0xef, 0x6e, 0x03, 0x8d, 0x15, 0xa7, 0xf6, 0xe2, 0xc2, 0xba, 0xbb, 0x08, 0xc8, 0x7d, 0x20, 0xae,
0xaf, 0xe8, 0x5b, 0x66, 0xa3, 0xaf, 0x38, 0xb5, 0x5e, 0x61, 0x5b, 0xe6, 0x1e, 0xb7, 0xb6, 0xe5,
0xc2, 0x6d, 0x99, 0x35, 0xb0, 0x77, 0x03, 0xbb, 0xbd, 0x38, 0x82, 0xea, 0x7d, 0x7e, 0x67, 0xb2,
0xe2, 0xe7, 0xde, 0x75, 0xdc, 0xe3, 0xf0, 0x7a, 0x05, 0xb3, 0x42, 0xee, 0x12, 0xd6, 0xa0, 0x8d,
0x25, 0x38, 0xb5, 0x65, 0xe6, 0x8d, 0xb0, 0x68, 0x6d, 0xb3, 0x53, 0x7b, 0x71, 0x98, 0x15, 0xed,
0x3e, 0x73, 0x72, 0x5f, 0xb8, 0xc6, 0x8a, 0xeb, 0x2b, 0xfc, 0xd1, 0x38, 0x67, 0xd6, 0x2b, 0x01,
0x4f, 0xf5, 0xd1, 0x38, 0x67, 0xb2, 0xe2, 0xb6, 0xcc, 0x56, 0x30, 0x8e, 0xc6, 0xb1, 0xdb, 0x9c,
0xca, 0x8a, 0xeb, 0x2b, 0x66, 0xb8, 0xbd, 0x38, 0x2b, 0x56, 0x78, 0xbd, 0x38, 0x67, 0xb7, 0xcc,
0x7e, 0x4f, 0x37, 0x04, 0x3b, 0x69, 0xbb, 0x6c, 0x64, 0xc5, 0xa9, 0xbd, 0x38, 0xcd, 0x57, 0xe8,
0xfc, 0x7b, 0x31, 0xa4, 0x54, 0xae, 0x20, 0xa7, 0xfa, 0x4a, 0x4f, 0xe5, 0x39, 0x43, 0x3a, 0xff,
0x8e, 0x47, 0x70, 0xdd, 0x7a, 0x45, 0x3f, 0x72, 0x8b, 0x21, 0xf6, 0xce, 0x67, 0xc6, 0xb3, 0x9d,
0xc8, 0x4a, 0xad, 0xbb, 0x9d, 0x9c, 0xca, 0x0a, 0x1d, 0x8b, 0xd3, 0x9e, 0x01, 0xf3, 0x3b, 0xbe,
0xbf, 0x42, 0xa0, 0x9f, 0x57, 0x65, 0xe1, 0x5c, 0x56, 0xe8, 0x73, 0xa9, 0xc6, 0xbb, 0x34, 0x5c,
0x56, 0x48, 0xeb, 0xac, 0xbf, 0xa3, 0xc7, 0xb9, 0x2d, 0x33, 0x01, 0xbe, 0x0f, 0x4c, 0x3f, 0x0a,
0x47, 0xe0, 0xd4, 0x5e, 0x1c, 0x81, 0xf6, 0xc4, 0xbb, 0x9e, 0x15, 0x67, 0xfb, 0x8a, 0x15, 0x2b,
0x4e, 0xae, 0x57, 0xb4, 0xfa, 0x56, 0x3f, 0x9b, 0xea, 0x74, 0x56, 0xd8, 0xb6, 0x79, 0x92, 0xe0,
0xb2, 0xa2, 0x81, 0x3e, 0x4d, 0xaf, 0x97, 0x3a, 0xf5, 0x99, 0x8f, 0xea, 0x70, 0x9f, 0x0f, 0xaa,
0x0e, 0xf7, 0x09, 0x3b, 0x1e, 0xdc, 0xa7, 0x31, 0xf9, 0xac, 0xb4, 0xee, 0xd9, 0x5b, 0x3b, 0x3f,
0xb9, 0xcb, 0x7b, 0xd2, 0x7a, 0x62, 0xc7, 0x7e, 0xea, 0x9f, 0xf9, 0x53, 0xdf, 0x73, 0xda, 0x3b,
0x66, 0xef, 0xb1, 0x04, 0xef, 0xa9, 0xfc, 0xc3, 0xda, 0x3b, 0x54, 0xcb, 0x3a, 0x3a, 0x78, 0xbd,
0xdf, 0xda, 0xcf, 0xf4, 0xef, 0xa8, 0xbd, 0xeb, 0x3b, 0xe7, 0xcd, 0x43, 0x35, 0xcb, 0xaa, 0x03,
0xdb, 0x8d, 0xaf, 0x8b, 0xfd, 0xca, 0xfe, 0xde, 0x68, 0x26, 0x84, 0xed, 0xf9, 0x6d, 0x1e, 0xc6,
0xf9, 0x9a, 0x89, 0xf1, 0x1d, 0x13, 0x3a, 0xc6, 0x77, 0x34, 0xbc, 0x57, 0xc9, 0xc7, 0x59, 0x56,
0xf0, 0x35, 0xd2, 0x68, 0x29, 0x24, 0x73, 0x4a, 0x73, 0x8e, 0xa3, 0xd5, 0x59, 0x71, 0xa6, 0x08,
0x87, 0xb4, 0x8d, 0xe3, 0xb0, 0x32, 0xce, 0x77, 0x48, 0x5b, 0xeb, 0x6c, 0xaa, 0xe4, 0x63, 0x62,
0xee, 0x0f, 0xd4, 0x4b, 0x8c, 0x99, 0x41, 0x8d, 0xb9, 0x41, 0x79, 0xb9, 0x4a, 0x79, 0xd9, 0x4a,
0x79, 0xd9, 0xb0, 0x48, 0x0e, 0x25, 0x3e, 0x2b, 0x53, 0x98, 0x3c, 0x4b, 0xe6, 0x2c, 0x58, 0x6c,
0xd6, 0xab, 0xda, 0x99, 0x4a, 0x79, 0xf9, 0xb5, 0xf4, 0x92, 0x20, 0x27, 0xbb, 0x93, 0x79, 0x76,
0xb9, 0xbb, 0x8e, 0x5c, 0xa5, 0x32, 0x27, 0xfb, 0x19, 0x2f, 0x2f, 0xa9, 0x6c, 0x91, 0x39, 0x0d,
0x47, 0x59, 0x81, 0x3a, 0x58, 0x31, 0x67, 0x4c, 0xd3, 0x67, 0x48, 0xab, 0x95, 0xa9, 0xd4, 0xca,
0x1a, 0xbd, 0x96, 0x99, 0x13, 0x7d, 0xc6, 0x4f, 0x92, 0x9f, 0x0e, 0xe7, 0xa0, 0xab, 0x9e, 0xab,
0x34, 0x24, 0x45, 0xfb, 0xea, 0xc9, 0x4b, 0xaa, 0xe9, 0xb1, 0x59, 0xf6, 0xc8, 0x72, 0x6d, 0x56,
0xb4, 0x08, 0x5e, 0xcb, 0x99, 0x47, 0xbf, 0x95, 0xf6, 0x5a, 0x99, 0x4a, 0xcd, 0x5a, 0x56, 0x56,
0xf7, 0x98, 0x32, 0xf6, 0xd1, 0xf9, 0x42, 0x6d, 0x6e, 0x99, 0x9f, 0x83, 0xd4, 0xa8, 0x47, 0x33,
0x75, 0x56, 0xcb, 0x69, 0x5a, 0xb2, 0x63, 0xb6, 0x39, 0xff, 0x24, 0xcd, 0xa8, 0x58, 0x8b, 0x15,
0x6d, 0xb4, 0x87, 0xe6, 0x57, 0xd4, 0xf2, 0x2c, 0xf2, 0x32, 0x95, 0x46, 0xfb, 0xb4, 0x8c, 0xa6,
0x84, 0x15, 0xaa, 0x55, 0xcd, 0x6a, 0x63, 0x3e, 0x53, 0x3f, 0x33, 0x5f, 0x48, 0xee, 0xfd, 0xc2,
0x99, 0x40, 0x25, 0x0f, 0x3f, 0xb7, 0xa8, 0x5e, 0x4f, 0xcb, 0x49, 0xc9, 0xcb, 0x69, 0x4a, 0xf6,
0x95, 0xbd, 0x58, 0xcf, 0x98, 0x7f, 0x12, 0x9f, 0x1f, 0x62, 0xc7, 0xf6, 0xb0, 0x62, 0xce, 0x54,
0x2a, 0x0b, 0x92, 0x47, 0xcb, 0x68, 0x8a, 0xaf, 0x0d, 0x7a, 0x24, 0xd5, 0xad, 0xb6, 0x62, 0x85,
0xe4, 0x25, 0xa5, 0x5e, 0x47, 0xca, 0x22, 0xe7, 0xd3, 0x6f, 0xc8, 0x37, 0xaa, 0xe9, 0x9d, 0x5f,
0x25, 0xa7, 0x29, 0xde, 0x17, 0xe7, 0x2a, 0x2d, 0x7b, 0x7b, 0x39, 0xff, 0x24, 0xfe, 0xc3, 0xff,
0xe7, 0x45, 0xea, 0xb3, 0xdb, 0xcb, 0x8a, 0x3e, 0x53, 0x29, 0xf6, 0x45, 0xf3, 0x53, 0x4e, 0xb5,
0xad, 0xc6, 0xb3, 0xe9, 0x41, 0xce, 0x15, 0x44, 0x33, 0xc1, 0x92, 0xb9, 0x65, 0x8d, 0x15, 0x5c,
0xdb, 0xd3, 0xcc, 0xac, 0x46, 0x3d, 0x89, 0x9b, 0xd3, 0x94, 0xef, 0x17, 0xa4, 0xce, 0xc3, 0xe7,
0x11, 0x6f, 0xd1, 0xea, 0xb7, 0x7a, 0x59, 0xa1, 0xd9, 0x6c, 0xcd, 0x59, 0x6d, 0xf5, 0x4f, 0x33,
0xf5, 0xe8, 0xc6, 0x57, 0x79, 0x47, 0xc7, 0xb3, 0x5a, 0xcf, 0x4a, 0xa0, 0x52, 0xdb, 0xea, 0xe7,
0x96, 0xb5, 0x8c, 0x9f, 0xda, 0xb3, 0x76, 0x92, 0x49, 0x6f, 0xea, 0x31, 0x92, 0x29, 0xa7, 0x69,
0x34, 0x68, 0xcc, 0x4b, 0x4a, 0xfd, 0x22, 0xc0, 0x3c, 0x47, 0xa2, 0xd5, 0x6d, 0xd6, 0xac, 0x04,
0x74, 0xb5, 0x2d, 0x2e, 0x0f, 0xdf, 0xbf, 0x48, 0xbf, 0xf9, 0x99, 0x4a, 0xc3, 0x82, 0xdf, 0xc3,
0x8e, 0xaf, 0xf2, 0x8e, 0x8e, 0x6f, 0xf5, 0x7c, 0xe6, 0x0a, 0x0a, 0x58, 0xb4, 0xcc, 0xbc, 0x2c,
0xa0, 0xd6, 0x6d, 0x9c, 0xfa, 0xcd, 0xe4, 0x34, 0xe5, 0xb5, 0x98, 0x34, 0x33, 0xa5, 0x39, 0x1b,
0x69, 0x2d, 0x56, 0xf8, 0x39, 0xc4, 0xad, 0x6d, 0x64, 0x5b, 0x72, 0xab, 0x7e, 0x07, 0xdd, 0xb3,
0x5b, 0x67, 0x35, 0x73, 0xfd, 0x54, 0xce, 0x08, 0xaf, 0x9f, 0x63, 0xcc, 0x02, 0x6a, 0xd6, 0xa3,
0xbd, 0x33, 0x9a, 0x39, 0x5e, 0x9f, 0x73, 0xde, 0xcf, 0x48, 0x8c, 0x25, 0xd5, 0xcf, 0x0a, 0x2f,
0xdf, 0x7c, 0x35, 0x1b, 0xcd, 0xd9, 0x4a, 0xad, 0x8e, 0x4e, 0x9f, 0x89, 0xd5, 0x4e, 0xd9, 0x39,
0xed, 0xd8, 0xe3, 0x6f, 0x52, 0x54, 0xd8, 0x9c, 0x52, 0x9a, 0x10, 0xdf, 0x35, 0xc7, 0x92, 0xe6,
0x8c, 0x21, 0xb4, 0xf6, 0xcc, 0x1a, 0xfd, 0x6e, 0xfb, 0xd1, 0x9c, 0x71, 0x39, 0xda, 0x8e, 0xb2,
0x31, 0x66, 0xf5, 0x9a, 0xa8, 0xf1, 0x77, 0x5f, 0x90, 0xd2, 0xfc, 0xe5, 0x25, 0x7e, 0xec, 0x4a,
0xeb, 0x28, 0x3b, 0x80, 0xb2, 0x62, 0xae, 0xeb, 0xfd, 0x75, 0x46, 0x66, 0x8d, 0xbd, 0xfb, 0xc2,
0x3a, 0xba, 0xb3, 0x1f, 0x2b, 0xe6, 0xf7, 0x4d, 0xd0, 0x1e, 0x4e, 0xad, 0xb8, 0xb3, 0xd1, 0x77,
0x5f, 0x58, 0x47, 0x77, 0xf6, 0x63, 0x65, 0x92, 0x80, 0x59, 0x61, 0x23, 0xb3, 0x28, 0x27, 0x9e,
0xd4, 0xc7, 0x9d, 0x34, 0x9e, 0xe4, 0x45, 0x6b, 0xe6, 0xb7, 0x54, 0x50, 0x3d, 0x1a, 0x7b, 0x98,
0xdf, 0x56, 0x61, 0x47, 0x56, 0x70, 0xaf, 0x92, 0x8d, 0xcc, 0x24, 0x4e, 0xdc, 0xb9, 0xa9, 0x12,
0x77, 0xf2, 0xe2, 0x49, 0x36, 0x5a, 0x23, 0xec, 0xb1, 0xb1, 0x1f, 0x65, 0x83, 0xf7, 0xfe, 0x03,
0xd2, 0xbf, 0xb7, 0x23, 0x2b, 0x6c, 0x24, 0x27, 0x97, 0xea, 0x94, 0x6a, 0x71, 0x27, 0x2f, 0x9e,
0x64, 0xa3, 0x35, 0xa2, 0xc7, 0xc6, 0x7e, 0x46, 0x56, 0xcc, 0x59, 0x2e, 0xec, 0xc9, 0x4a, 0xa0,
0x66, 0x64, 0x36, 0x54, 0x61, 0xc5, 0xfc, 0x8e, 0x0c, 0x7d, 0xb4, 0x66, 0x3d, 0x96, 0xc1, 0xb2,
0x42, 0x23, 0x7a, 0x7b, 0xb3, 0x52, 0x2b, 0x32, 0xd3, 0xe2, 0x4e, 0xde, 0x3b, 0x32, 0x74, 0x31,
0xa6, 0x29, 0xf6, 0xa3, 0xb5, 0x2c, 0x3b, 0xb2, 0xa7, 0xcf, 0x13, 0x63, 0x4f, 0x56, 0x1a, 0x89,
0xcc, 0xba, 0x38, 0xef, 0xc8, 0xa8, 0xf7, 0xbd, 0x19, 0x56, 0x71, 0xa1, 0x5d, 0x59, 0xa9, 0x16,
0x99, 0xe9, 0x47, 0xfb, 0xf5, 0xf1, 0xa4, 0xdf, 0xa8, 0x55, 0xe3, 0x2d, 0x15, 0xfc, 0xb8, 0xd0,
0x6e, 0xac, 0xd8, 0xa9, 0xc7, 0xdf, 0x6a, 0x3e, 0x28, 0x2b, 0xcd, 0x89, 0xc9, 0x9a, 0x01, 0xfb,
0xdc, 0xa1, 0xd6, 0xf8, 0xac, 0xf0, 0x58, 0xc2, 0xbd, 0x17, 0xcb, 0x85, 0x0b, 0x17, 0x2e, 0x5c,
0xb8, 0x70, 0xe1, 0xc2, 0x85, 0x0b, 0x17, 0x2e, 0x5c, 0xb8, 0x70, 0xb1, 0x63, 0xf1, 0x3f, 0x53,
0xd1, 0xe8, 0xeb, 0x3a, 0x9b, 0x00, 0x00
};
#endif // SCUMM_HE_MOONBASE_MAP_DATA_H

View File

@@ -0,0 +1,797 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "scumm/he/moonbase/map_katton.h"
namespace Scumm {
KattonGenerator::KattonGenerator(int seed) {
_seed = seed;
}
int KattonGenerator::getRandomNumber() {
// This is the exact linear congruential generator
// algorithm used on MSVCRT (Windows Visual C++ Runtime), with
// RAND_MAX being 0x7fff (32767). This is implemented here
// to match the RNG between the original Moonbase Console
// program and ScummVM.
//
// (Common::RandomSource uses Xorshift and uses unsigned
// integers compared to MSVCRT's rand)
_seed = _seed * 214013 + 2531011;
return (_seed >> 16) & 0x7fff;
}
MapFile *KattonGenerator::generateMap(int water, int tileSet, int mapSize, int energy, int terrain) {
_size = mapSize;
_tileset = tileSet;
int inEnergy = energy;
int inTerrain = terrain;
int inWater = water;
if (!(inEnergy == 0)) {
inEnergy = inEnergy * 14 + plusminus(14);
}
if (!(inTerrain == 0)) {
inTerrain = inTerrain * 14 + plusminus(14);
}
if (!(inWater == 0)) {
inWater = inWater * 14 + plusminus(14);
}
float terrainpercent = ((float)inTerrain) / 100;
float waterpercent = ((float)inWater) / 100;
//****************************************Let the generation BEGIN!
fillboards(0);
// used everywhere
int i, j, k, l, x, y, z;
// used in making the basic landmass shapes
int numsplotches, length;
// used in making water
int numwaterplaces = 0, numwatersplotches, multiplier;
short int goodwater[1600][2];
// used in making energy
int maxnumclose = 0, maxnumfar = 0, maxnumrand = 0, smallmed = 0, goodplaceClose[300][2], goodplaceFar[300][2], numplaceClose, numplaceFar, placeFar[3], placeClose[2], counter, counterpools;
int stringiness, randomx, randomy;
//****************************************Make the basic landmass shapes.
numsplotches = (int)(_size / 4) + (int)((terrainpercent - 0.5) * _size / 2);
for (i = 0; i < numsplotches; i++) {
length = (int)(((1 - waterpercent) / 6 * _size * _size) / numsplotches);
length = length + plusminus((int)(length / 2));
stringiness = 1 + plusminus(1);
randomx = getRandomNumber() % _size;
randomy = getRandomNumber() % _size;
randomsplotch(length, 1 + stringiness, 2, randomx, randomy);
}
for (i = 0; i < (int)numsplotches / 4; i++) {
length = (int)(((1 - waterpercent) / 3 * _size * _size) / numsplotches);
length = length + plusminus((int)(length / 4));
stringiness = 1 + plusminus(1);
randomx = getRandomNumber() % _size;
randomy = getRandomNumber() % _size;
randomsplotch(length, stringiness, 1, randomx, randomy);
}
for (i = 0; i < (int)numsplotches / 2; i++) {
length = (int)(_size + plusminus((int)(_size / 2)));
stringiness = 1 + plusminus(1);
randomx = getRandomNumber() % _size;
randomy = getRandomNumber() % _size;
randomsplotch(length, stringiness, 0, randomx, randomy);
}
for (i = 0; i < (int)waterpercent * _size; i++) {
length = (int)(_size + plusminus((int)(_size / 2)));
stringiness = 1 + plusminus(1);
randomx = getRandomNumber() % _size;
randomy = getRandomNumber() % _size;
randomsplotch(length, stringiness, 0, randomx, randomy);
}
//****************************************Fatten up the landmasses
// make the highhills bigger
fattenall(-3, 2, NEVER_USED_NUM, TEMP_REPLACEMENT_NUMA);
replacenum(TEMP_REPLACEMENT_NUMA, 2);
// make the lowlands bigger, depending on wateriness
fattenall(-((int)(waterpercent * 3)), 0, NEVER_USED_NUM, TEMP_REPLACEMENT_NUMA);
replacenum(TEMP_REPLACEMENT_NUMA, 2);
// surround the high hills with at least one med
fattenall(2, 2, NEVER_USED_NUM, 1);
// fatten the medium hills
fattenall(-3, 1, 2, TEMP_REPLACEMENT_NUMA);
replacenum(TEMP_REPLACEMENT_NUMA, 1);
//****************************************Smooth out/rough up the landmasses
randomflip((int)(terrainpercent * terrainpercent * _size * _size / 4 + _size / 4), inWater);
//****************************************Make the start locations
findstartloc();
//****************************************Put down some water
for (i = 0; i < _size; i += 2) {
for (j = 0; j < _size; j += 2) {
if (goodforwater(i, j)) {
goodwater[numwaterplaces][0] = i;
goodwater[numwaterplaces][1] = j;
numwaterplaces++;
}
}
}
numwatersplotches = (int)(_size * waterpercent * waterpercent * 5);
if (numwaterplaces <= numwatersplotches) {
numwatersplotches = numwaterplaces;
}
if (numwatersplotches >= 1) {
multiplier = (int)((float)numwaterplaces / (float)numwatersplotches);
for (i = 0; i < numwatersplotches; i++) {
z = getRandomNumber() % multiplier + i * multiplier;
x = goodwater[z][0];
y = goodwater[z][1];
length = _size + plusminus(_size / 2);
stringiness = getRandomNumber() % 3;
randomwater(length, stringiness, x, y);
}
}
//****************************************Put down the energy
if (inEnergy >= 90) {
maxnumclose = 2;
maxnumfar = 3;
maxnumrand = (int)(_size * _size / 150);
smallmed = 2;
} else if (inEnergy >= 75) {
maxnumclose = 2;
maxnumfar = 1;
maxnumrand = (int)(_size * _size / 250) + 4;
smallmed = 3;
} else if (inEnergy >= 60) {
maxnumclose = 1;
maxnumfar = 2;
maxnumrand = (int)(_size * _size / 250);
smallmed = 4;
} else if (inEnergy >= 45) {
maxnumclose = 1;
maxnumfar = 1;
maxnumrand = (int)(_size * _size / 250);
smallmed = 6;
} else if (inEnergy >= 30) {
maxnumclose = 1;
maxnumfar = 0;
maxnumrand = (int)(_size * _size / 350) + 2;
smallmed = 8;
} else if (inEnergy >= 15) {
maxnumclose = 0;
maxnumfar = 1;
maxnumrand = (int)(_size * _size / 450);
smallmed = 10;
} else if (inEnergy >= 0) {
maxnumclose = 0;
maxnumfar = 0;
maxnumrand = 0;
smallmed = 1;
}
for (i = 0; i < 4; i++) {
numplaceClose = 0;
numplaceFar = 0;
for (j = 0; j < _size; j++) {
for (k = 0; k < _size; k++) {
if ((goodforenergy(j, k, 0)) && (distance(_startloc[i][0], _startloc[i][1], j, k) >= (int)(_size / 10)) && (distance(_startloc[i][0], _startloc[i][1], j, k) <= (int)(_size / 8))) {
goodplaceClose[numplaceClose][0] = j;
goodplaceClose[numplaceClose][1] = k;
numplaceClose++;
} else if ((goodforenergy(j, k, 0)) && (distance(_startloc[i][0], _startloc[i][1], j, k) >= (int)(_size / 7)) && (distance(_startloc[i][0], _startloc[i][1], j, k) <= (int)(_size / 5))) {
goodplaceFar[numplaceFar][0] = j;
goodplaceFar[numplaceFar][1] = k;
numplaceFar++;
}
}
}
if (numplaceClose >= 1) {
placeClose[0] = getRandomNumber() % (int)(numplaceClose / 2) + 1;
}
if (numplaceClose >= 2) {
placeClose[1] = getRandomNumber() % (int)(numplaceClose / 2) + (int)(numplaceClose / 2) - 1;
}
if (numplaceClose >= maxnumclose) {
for (l = 0; l < maxnumclose; l++) {
_special[goodplaceClose[placeClose[l]][0]][goodplaceClose[placeClose[l]][1]] = 100;
}
} else {
for (l = 0; l < numplaceClose; l++) {
_special[goodplaceClose[placeClose[l]][0]][goodplaceClose[placeClose[l]][1]] = 100;
}
}
if (numplaceFar >= 1) {
placeFar[0] = getRandomNumber() % (int)(numplaceFar / 3) + 1;
}
if (numplaceFar >= 2) {
placeFar[1] = getRandomNumber() % (int)(numplaceFar / 3) + (int)(numplaceClose / 3);
}
if (numplaceFar >= 3) {
placeFar[2] = getRandomNumber() % (int)(numplaceFar / 3) + (int)(2 * numplaceClose / 3) - 1;
}
if (numplaceFar >= maxnumfar) {
for (l = 0; l < maxnumfar; l++) {
_special[goodplaceFar[placeFar[l]][0]][goodplaceFar[placeFar[l]][1]] = 100;
}
} else {
for (l = 0; l < numplaceFar; l++) {
_special[goodplaceFar[placeFar[l]][0]][goodplaceFar[placeFar[l]][1]] = 100;
}
}
}
counter = 0;
counterpools = 4 * (maxnumfar + maxnumclose);
for (k = 0; k < maxnumrand && counterpools < 50; k++) {
do {
i = getRandomNumber() % _size;
j = getRandomNumber() % _size;
counter++;
} while (!((distance(i, j, _startloc[0][0], _startloc[0][1]) >= 10) && (distance(i, j, _startloc[1][0], _startloc[1][1]) >= 10) && (distance(i, j, _startloc[2][0], _startloc[2][1]) >= 10) && (distance(i, j, _startloc[3][0], _startloc[3][1]) >= 10) && (goodforenergy(i, j, 1)) && (counter < 5000)));
if (getRandomNumber() % smallmed == 0) {
_special[i][j] = 200;
counterpools++;
} else {
_special[i][j] = 100;
counterpools++;
}
}
//****************************************Do that saving thing that you do, BABY!
MIF mif = MIF();
Common::sprintf_s(mif._name, "Katton %04X", (uint16)_seed);
mif._dimension = _size;
mif._mapType = _tileset;
for (j = 0; j < _size; j++) {
for (i = 0; i < _size; i++)
mif._cornerMap[i][j] = _board[i][j];
for (i = 0; i < _size; i++)
if (_special[i][j] == 0)
mif._centerMap[i][j] = '.';
else if (_special[i][j] == -1)
mif._centerMap[i][j] = 'W';
else if (_special[i][j] == 100)
mif._centerMap[i][j] = 'S';
else if (_special[i][j] == 200)
mif._centerMap[i][j] = 'M';
else if (_special[i][j] == 300)
mif._centerMap[i][j] = 'L';
else
mif._centerMap[i][j] = -_special[i][j];
}
// Generate new map:
MapFile *map = new MapFile();
mif.generateMap(map);
return map;
}
int KattonGenerator::distance(int x1, int y1, int x2, int y2) {
int dx, dy, disp;
dx = min((abs(x1 - x2)), (abs(x1 + _size - x2)), (abs(x2 + _size - x1)));
dy = min((abs(y1 - y2)), (abs(y1 + _size - y2)), (abs(y2 + _size - y1)));
disp = (int)sqrt((double)(dx * dx + dy * dy));
return disp;
}
int KattonGenerator::min(int a, int b, int c) {
if ((a <= b) && (a <= c)) {
return a;
} else if ((b < a) && (b <= c)) {
return b;
} else if ((c < a) && (c < b)) {
return c;
} else {
return a;
}
}
int KattonGenerator::goodforenergy(int x, int y, int poolsize) {
switch (poolsize) {
case 0:
if ((_board[x][y] == _board[findcoord(x, +1)][y]) && (_board[findcoord(x, +1)][y] == _board[x][findcoord(y, +1)]) && (_board[x][findcoord(y, +1)] == _board[findcoord(x, +1)][findcoord(y, +1)])) {
//check main map
if (_special[x][y] == 0) { //specials are clear
return 1;
} else {
return 0;
}
} else {
return 0;
}
case 1:
if ((_board[x][y] == _board[findcoord(x, +1)][y]) && (_board[findcoord(x, +1)][y] == _board[x][findcoord(y, +1)]) && (_board[x][findcoord(y, +1)] == _board[findcoord(x, +1)][findcoord(y, +1)])) { //check main map
if ((_special[x][y] == 0) && (_special[findcoord(x, +1)][y] == 0) && (_special[x][findcoord(y, +1)] == 0) && (_special[findcoord(x, +1)][findcoord(y, +1)] == 0)) { //specials are clear
return 1;
} else {
return 0;
}
} else {
return 0;
}
default:
return 0;
}
}
int KattonGenerator::plusminus(int max) {
int result = getRandomNumber() % (max + 1);
if (getRandomNumber() % 2 == 0) {
result *= (-1);
}
return result;
}
int KattonGenerator::fillboards(int num) {
int i, j;
for (i = 0; i < _size; i++) {
for (j = 0; j < _size; j++) {
_board[i][j] = num;
_special[i][j] = num;
}
}
return 0;
}
int KattonGenerator::randomplace(int numberofplaces, int placer) {
int i, randx, randy;
for (i = 0; i < numberofplaces; i++) {
randx = (getRandomNumber() % _size);
randy = (getRandomNumber() % _size);
_board[randx][randy] = placer;
}
return 0;
}
int KattonGenerator::randomsplotch(int length, int stringiness, int placer, int x, int y) {
int currx, curry, direction = 10, prevdirection, movex = 0, movey = 0, i = 0;
currx = x;
curry = y;
while (i <= length) {
_board[currx][curry] = placer;
prevdirection = direction;
direction = (getRandomNumber() % 4);
if ((((direction + 2) == prevdirection) || ((direction - 2) == prevdirection)) && (stringiness == 2)) {
direction = prevdirection;
}
if (!((((direction + 2) == prevdirection) || ((direction - 2) == prevdirection)) && (stringiness == 1))) {
switch (direction) {
case 0:
movex = 0;
movey = 1;
break;
case 1:
movex = 1;
movey = 0;
break;
case 2:
movex = 0;
movey = -1;
break;
case 3:
movex = -1;
movey = 0;
break;
}
currx = findcoord(currx, movex);
curry = findcoord(curry, movey);
i++;
}
}
return 0;
}
int KattonGenerator::findcoord(int value, int move) {
move = move % _size;
int final = value + move;
if (final < 0) {
final = _size + final;
}
if (final >= _size) {
final = final % _size;
}
return final;
}
int KattonGenerator::replacenum(int replacee, int replacer) {
int i, j;
for (j = 0; j < _size; j++) {
for (i = 0; i < _size; i++) {
if (_board[i][j] == replacee) {
_board[i][j] = replacer;
}
}
}
return 0;
}
int KattonGenerator::fattenall(int howfat, int middle, int ignorer, int replacer) {
int i, j, temp;
for (j = 0; j < _size; j++) {
for (i = 0; i < _size; i++) {
if (_board[i][j] == middle) {
if (howfat <= 0) {
temp = (int)(abs(howfat) + plusminus(2));
if (temp <= 1) {
temp = 2;
} else if (temp >= 6) {
temp = 5;
}
fattenone(i, j, temp, middle, ignorer, replacer);
} else {
fattenone(i, j, howfat, middle, ignorer, replacer);
}
}
}
}
return 0;
}
int KattonGenerator::fattenone(int x, int y, int howfat, int middle, int ignorer, int replacer) {
if (howfat == -100) {
_board[x][y] = replacer;
_board[findcoord(x, +1)][y] = replacer;
_board[x][findcoord(y, +1)] = replacer;
_board[findcoord(x, +1)][findcoord(y, +1)] = replacer;
}
if (howfat >= 1) {
_board[x][findcoord(y, -1)] = ((_board[x][findcoord(y, -1)] == middle) || (_board[x][findcoord(y, -1)] == ignorer)) ? _board[x][findcoord(y, -1)] : replacer;
_board[x][findcoord(y, +1)] = ((_board[x][findcoord(y, +1)] == middle) || (_board[x][findcoord(y, +1)] == ignorer)) ? _board[x][findcoord(y, +1)] : replacer;
_board[findcoord(x, -1)][y] = ((_board[findcoord(x, -1)][y] == middle) || (_board[findcoord(x, -1)][y] == ignorer)) ? _board[findcoord(x, -1)][y] : replacer;
_board[findcoord(x, +1)][y] = ((_board[findcoord(x, +1)][y] == middle) || (_board[findcoord(x, +1)][y] == ignorer)) ? _board[findcoord(x, +1)][y] : replacer;
}
if (howfat >= 2) {
_board[findcoord(x, -1)][findcoord(y, -1)] = ((_board[findcoord(x, -1)][findcoord(y, -1)] == middle) || (_board[findcoord(x, -1)][findcoord(y, -1)] == ignorer)) ? _board[findcoord(x, -1)][findcoord(y, -1)] : replacer;
_board[findcoord(x, -1)][findcoord(y, +1)] = ((_board[findcoord(x, -1)][findcoord(y, +1)] == middle) || (_board[findcoord(x, -1)][findcoord(y, +1)] == ignorer)) ? _board[findcoord(x, -1)][findcoord(y, +1)] : replacer;
_board[findcoord(x, +1)][findcoord(y, -1)] = ((_board[findcoord(x, +1)][findcoord(y, -1)] == middle) || (_board[findcoord(x, +1)][findcoord(y, -1)] == ignorer)) ? _board[findcoord(x, +1)][findcoord(y, -1)] : replacer;
_board[findcoord(x, +1)][findcoord(y, +1)] = ((_board[findcoord(x, +1)][findcoord(y, +1)] == middle) || (_board[findcoord(x, +1)][findcoord(y, +1)] == ignorer)) ? _board[findcoord(x, +1)][findcoord(y, +1)] : replacer;
}
if (howfat >= 3) {
_board[x][findcoord(y, -2)] = ((_board[x][findcoord(y, -2)] == middle) || (_board[x][findcoord(y, -2)] == ignorer)) ? _board[x][findcoord(y, -2)] : replacer;
_board[x][findcoord(y, +2)] = ((_board[x][findcoord(y, +2)] == middle) || (_board[x][findcoord(y, +2)] == ignorer)) ? _board[x][findcoord(y, +2)] : replacer;
_board[findcoord(x, -2)][y] = ((_board[findcoord(x, -2)][y] == middle) || (_board[findcoord(x, -2)][y] == ignorer)) ? _board[findcoord(x, -2)][y] : replacer;
_board[findcoord(x, +2)][y] = ((_board[findcoord(x, +2)][y] == middle) || (_board[findcoord(x, +2)][y] == ignorer)) ? _board[findcoord(x, +2)][y] : replacer;
}
if (howfat >= 4) {
_board[findcoord(x, -1)][findcoord(y, -2)] = ((_board[findcoord(x, -1)][findcoord(y, -2)] == middle) || (_board[findcoord(x, -1)][findcoord(y, -2)] == ignorer)) ? _board[findcoord(x, -1)][findcoord(y, -2)] : replacer;
_board[findcoord(x, -1)][findcoord(y, +2)] = ((_board[findcoord(x, -1)][findcoord(y, +2)] == middle) || (_board[findcoord(x, -1)][findcoord(y, +2)] == ignorer)) ? _board[findcoord(x, -1)][findcoord(y, +2)] : replacer;
_board[findcoord(x, +1)][findcoord(y, -2)] = ((_board[findcoord(x, +1)][findcoord(y, -2)] == middle) || (_board[findcoord(x, +1)][findcoord(y, -2)] == ignorer)) ? _board[findcoord(x, +1)][findcoord(y, -2)] : replacer;
_board[findcoord(x, +1)][findcoord(y, +2)] = ((_board[findcoord(x, +1)][findcoord(y, +2)] == middle) || (_board[findcoord(x, +1)][findcoord(y, +2)] == ignorer)) ? _board[findcoord(x, +1)][findcoord(y, +2)] : replacer;
_board[findcoord(x, -2)][findcoord(y, -1)] = ((_board[findcoord(x, -2)][findcoord(y, -1)] == middle) || (_board[findcoord(x, -2)][findcoord(y, -1)] == ignorer)) ? _board[findcoord(x, -2)][findcoord(y, -1)] : replacer;
_board[findcoord(x, -2)][findcoord(y, +1)] = ((_board[findcoord(x, -2)][findcoord(y, +1)] == middle) || (_board[findcoord(x, -2)][findcoord(y, +1)] == ignorer)) ? _board[findcoord(x, -2)][findcoord(y, +1)] : replacer;
_board[findcoord(x, +2)][findcoord(y, -1)] = ((_board[findcoord(x, +2)][findcoord(y, -1)] == middle) || (_board[findcoord(x, +2)][findcoord(y, -1)] == ignorer)) ? _board[findcoord(x, +2)][findcoord(y, -1)] : replacer;
_board[findcoord(x, +2)][findcoord(y, +1)] = ((_board[findcoord(x, +2)][findcoord(y, +1)] == middle) || (_board[findcoord(x, +2)][findcoord(y, +1)] == ignorer)) ? _board[findcoord(x, +2)][findcoord(y, +1)] : replacer;
}
if (howfat >= 5) {
_board[findcoord(x, -2)][findcoord(y, -2)] = ((_board[findcoord(x, -2)][findcoord(y, -2)] == middle) || (_board[findcoord(x, -2)][findcoord(y, -2)] == ignorer)) ? _board[findcoord(x, -2)][findcoord(y, -2)] : replacer;
_board[findcoord(x, -2)][findcoord(y, +2)] = ((_board[findcoord(x, -2)][findcoord(y, +2)] == middle) || (_board[findcoord(x, -2)][findcoord(y, +2)] == ignorer)) ? _board[findcoord(x, -2)][findcoord(y, +2)] : replacer;
_board[findcoord(x, +2)][findcoord(y, -2)] = ((_board[findcoord(x, +2)][findcoord(y, -2)] == middle) || (_board[findcoord(x, +2)][findcoord(y, -2)] == ignorer)) ? _board[findcoord(x, +2)][findcoord(y, -2)] : replacer;
_board[findcoord(x, +2)][findcoord(y, +2)] = ((_board[findcoord(x, +2)][findcoord(y, +2)] == middle) || (_board[findcoord(x, +2)][findcoord(y, +2)] == ignorer)) ? _board[findcoord(x, +2)][findcoord(y, +2)] : replacer;
_board[x][findcoord(y, -3)] = ((_board[x][findcoord(y, -3)] == middle) || (_board[x][findcoord(y, -3)] == ignorer)) ? _board[x][findcoord(y, -3)] : replacer;
_board[x][findcoord(y, +3)] = ((_board[x][findcoord(y, +3)] == middle) || (_board[x][findcoord(y, +3)] == ignorer)) ? _board[x][findcoord(y, +3)] : replacer;
_board[findcoord(x, -3)][y] = ((_board[findcoord(x, -3)][y] == middle) || (_board[findcoord(x, -3)][y] == ignorer)) ? _board[findcoord(x, -3)][y] : replacer;
_board[findcoord(x, +3)][y] = ((_board[findcoord(x, +3)][y] == middle) || (_board[findcoord(x, +3)][y] == ignorer)) ? _board[findcoord(x, +3)][y] : replacer;
}
return 0;
}
int KattonGenerator::findstartloc() {
int temp, i, j, shiftx, shifty, secondshift;
int start[4][2];
shiftx = getRandomNumber() % _size;
shifty = getRandomNumber() % _size;
start[0][0] = findcoord((int)_size / 4, (plusminus(3) + shiftx));
start[0][1] = findcoord((int)_size / 4, (plusminus(3) + shifty));
start[1][0] = findcoord((int)3 * _size / 4, (plusminus(3) + shiftx));
start[1][1] = findcoord((int)_size / 4, (plusminus(3) + shifty));
start[2][0] = findcoord((int)_size / 4, (plusminus(3) + shiftx));
start[2][1] = findcoord((int)3 * _size / 4, (plusminus(3) + shifty));
start[3][0] = findcoord((int)3 * _size / 4, (plusminus(3) + shiftx));
start[3][1] = findcoord((int)3 * _size / 4, (plusminus(3) + shifty));
temp = getRandomNumber() % 2;
secondshift = getRandomNumber() % _size;
if (temp == 0) {
start[0][0] = findcoord(start[0][0], secondshift);
start[1][0] = findcoord(start[1][0], secondshift);
}
else {
start[1][1] = findcoord(start[1][1], secondshift);
start[3][1] = findcoord(start[3][1], secondshift);
}
temp = whatheightstartloc(start[0][0], start[0][1]);
fattenone(start[0][0], start[0][1], -100, temp, NEVER_USED_NUM, temp);
temp = whatheightstartloc(start[1][0], start[1][1]);
fattenone(start[1][0], start[1][1], -100, temp, NEVER_USED_NUM, temp);
temp = whatheightstartloc(start[2][0], start[2][1]);
fattenone(start[2][0], start[2][1], -100, temp, NEVER_USED_NUM, temp);
temp = whatheightstartloc(start[3][0], start[3][1]);
fattenone(start[3][0], start[3][1], -100, temp, NEVER_USED_NUM, temp);
for (j = 0; j < 4; j++) {
for (i = 0; i < 2; i++) {
_startloc[j][i] = start[j][i];
}
}
temp = getRandomNumber() % 4;
j = 4;
for (i = 0; i < 4; i++) {
if (temp == i) {
} else {
_startloc[j][0] = start[i][0];
_startloc[j][1] = start[i][1];
j++;
}
}
_startloc[7][0] = _startloc[4][0];
_startloc[7][1] = _startloc[4][1];
_startloc[8][0] = _startloc[6][0];
_startloc[8][1] = _startloc[6][1];
for (j = 9; j < 13; j++) {
for (i = 0; i < 2; i++) {
_startloc[j][i] = start[j - 9][i];
}
}
for (j = 13; j < 17; j++) {
for (i = 0; i < 2; i++) {
_startloc[j][i] = start[j - 13][i];
}
}
for (j = 17; j < 20; j++) {
for (i = 0; i < 2; i++) {
_startloc[j][i] = _startloc[j - 13][i];
}
}
// place on special map
for (i = 0; i < 4; i++) {
_special[_startloc[i][0]][_startloc[i][1]] += 1;
}
for (i = 4; i < 7; i++) {
_special[_startloc[i][0]][_startloc[i][1]] += 2;
}
for (i = 7; i < 9; i++) {
_special[_startloc[i][0]][_startloc[i][1]] += 4;
}
for (i = 9; i < 13; i++) {
_special[_startloc[i][0]][_startloc[i][1]] += 8;
}
for (i = 13; i < 17; i++) {
_special[_startloc[i][0]][_startloc[i][1]] += 16;
}
for (i = 17; i < 20; i++) {
_special[_startloc[i][0]][_startloc[i][1]] += 32;
}
return 0;
}
int KattonGenerator::whatheightstartloc(int x, int y) {
int heightfield[3] = {0, 0, 0};
heightfield[_board[findcoord(x, +2)][findcoord(y, -1)]]++;
heightfield[_board[findcoord(x, +2)][y]]++;
heightfield[_board[findcoord(x, +2)][findcoord(y, +1)]]++;
heightfield[_board[findcoord(x, +2)][findcoord(y, +2)]]++;
heightfield[_board[findcoord(x, -1)][findcoord(y, -1)]]++;
heightfield[_board[findcoord(x, -1)][y]]++;
heightfield[_board[findcoord(x, -1)][findcoord(y, +1)]]++;
heightfield[_board[findcoord(x, -1)][findcoord(y, +2)]]++;
heightfield[_board[x][findcoord(y, -1)]]++;
heightfield[_board[x][y]]++;
heightfield[_board[x][findcoord(y, +1)]]++;
heightfield[_board[x][findcoord(y, +2)]]++;
heightfield[_board[findcoord(x, +1)][findcoord(y, -1)]]++;
heightfield[_board[findcoord(x, +1)][y]]++;
heightfield[_board[findcoord(x, +1)][findcoord(y, +1)]]++;
heightfield[_board[findcoord(x, +1)][findcoord(y, +2)]]++;
if (heightfield[0] == 0) {
if (heightfield[1] >= heightfield[2]) {
return 1;
} else {
return 2;
}
} else if (heightfield[1] == 0) {
if (heightfield[0] >= heightfield[2]) {
return 0;
} else {
return 2;
}
} else if (heightfield[2] == 0) {
if (heightfield[1] >= heightfield[0]) {
return 1;
} else {
return 0;
}
} else {
return 1;
}
}
int KattonGenerator::goodforwater(int x, int y) {
if ((_board[x][y] == 0) && (_board[findcoord(x, +1)][y] == 0) && (_board[x][findcoord(y, +1)] == 0) && (_board[findcoord(x, +1)][findcoord(y, +1)] == 0)) { //check main map
if ((_special[x][y] <= 0) && (_special[x][findcoord(y, 1)] <= 0) && (_special[findcoord(x, 1)][findcoord(y, 1)] <= 0) && (_special[findcoord(x, 1)][y] <= 0) && (_special[findcoord(x, 1)][findcoord(y, -1)] <= 0) && (_special[x][findcoord(y, -1)] <= 0) && (_special[findcoord(x, -1)][findcoord(y, -1)] <= 0) && (_special[findcoord(x, -1)][y] <= 0) && (_special[findcoord(x, -1)][findcoord(y, 1)] <= 0)) { //specials are clear
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
int KattonGenerator::randomwater(int length, int stringiness, int x, int y) {
int currx, curry, direction = 10, prevdirection, i = 0;
currx = x;
curry = y;
while (i <= length) {
_special[currx][curry] = -1;
prevdirection = direction;
direction = (getRandomNumber() % 4);
if ((((direction + 2) == prevdirection) || ((direction - 2) == prevdirection)) && (stringiness == 2)) {
direction = prevdirection;
}
if (!((((direction + 2) == prevdirection) || ((direction - 2) == prevdirection)) && (stringiness == 1))) {
switch (direction) {
case 0: //north
if (goodforwater(currx, findcoord(curry, 1))) {
curry = findcoord(curry, 1);
}
break;
case 1: //east
if (goodforwater(findcoord(currx, 1), curry)) {
currx = findcoord(currx, 1);
}
break;
case 2: //south
if (goodforwater(currx, findcoord(curry, -1))) {
curry = findcoord(curry, -1);
}
break;
case 3: //west
if (goodforwater(findcoord(currx, -1), curry)) {
currx = findcoord(currx, -1);
}
break;
}
i++;
}
}
return 0;
}
int KattonGenerator::tileaverage(int x, int y, int threshold) {
int heightfield[3] = {0, 0, 0};
heightfield[_board[findcoord(x, -1)][findcoord(y, -1)]]++;
heightfield[_board[findcoord(x, -1)][y]]++;
heightfield[_board[findcoord(x, -1)][findcoord(y, +1)]]++;
heightfield[_board[x][findcoord(y, -1)]]++;
heightfield[_board[x][y]]++;
heightfield[_board[x][findcoord(y, +1)]]++;
heightfield[_board[findcoord(x, +1)][findcoord(y, -1)]]++;
heightfield[_board[findcoord(x, +1)][y]]++;
heightfield[_board[findcoord(x, +1)][findcoord(y, +1)]]++;
if ((heightfield[2] == 0) && (heightfield[1] < heightfield[0]) && (heightfield[0] >= threshold)) {
_board[x][y] = 0;
return 0;
} else if ((heightfield[0] == 0) && (heightfield[1] < heightfield[2]) && (heightfield[2] >= threshold)) {
_board[x][y] = 2;
return 2;
} else if (heightfield[1] >= threshold) {
_board[x][y] = 1;
return 1;
} else {
return 0;
}
}
int KattonGenerator::randomflip(int numberofplaces, int inWater) {
int i, x, y, temp;
for (i = 0; i < numberofplaces; i++) {
x = getRandomNumber() % _size;
y = getRandomNumber() % _size;
if (_board[x][y] == 0) {
if (inWater == 0) {
temp = 0;
} else {
temp = getRandomNumber() % inWater;
}
if (temp <= 50) {
_board[x][y] = 1;
}
} else if (_board[x][y] == 2) {
_board[x][y] = 1;
} else if (_board[x][y] == 1) {
temp = getRandomNumber() % 2;
int heightfield[3] = {0, 0, 0};
heightfield[_board[findcoord(x, -1)][findcoord(y, -1)]]++;
heightfield[_board[findcoord(x, -1)][y]]++;
heightfield[_board[findcoord(x, -1)][findcoord(y, +1)]]++;
heightfield[_board[x][findcoord(y, -1)]]++;
heightfield[_board[x][findcoord(y, +1)]]++;
heightfield[_board[findcoord(x, +1)][findcoord(y, -1)]]++;
heightfield[_board[findcoord(x, +1)][y]]++;
heightfield[_board[findcoord(x, +1)][findcoord(y, +1)]]++;
temp = getRandomNumber() % 2;
if (heightfield[0] == 0) {
_board[x][y] = 2;
} else if (heightfield[2] == 0) {
_board[x][y] = 0;
}
}
}
return 0;
}
} // End of namespace Scumm

View File

@@ -0,0 +1,77 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_MAP_KATTON_H
#define SCUMM_HE_MOONBASE_MAP_KATTON_H
#ifdef ENABLE_HE
#include "engines/scumm/he/moonbase/map_mif.h"
#define TEMP_REPLACEMENT_NUMA 5
#define NEVER_USED_NUM 99
namespace Scumm {
class KattonGenerator {
public:
KattonGenerator(int seed);
~KattonGenerator() = default;
MapFile *generateMap(int water, int tileSet, int mapSize, int energy, int terrain);
private:
int _seed = 0;
int _size = 0; // 32, 40, 48, or 56
int _tileset = 0;
int _startloc[20][2] = { {}, {} };
int _board[MAX_TILE_COUNT][MAX_TILE_COUNT] = { {}, {} };
int _special[MAX_TILE_COUNT][MAX_TILE_COUNT] = { {}, {} };
int getRandomNumber();
int min(int a, int b, int c);
int distance(int x1, int y1, int x2, int y2);
int plusminus(int max);
int fillboards(int num);
int randomplace(int numberofplaces, int placer);
int randomflip(int numberofplaces, int inWater);
// stringiness: 0 = no change/random, 1 = no back, 2 = back goes forwards
int randomsplotch(int length, int stringiness, int placer, int x, int y);
int goodforwater(int x, int y);
int randomwater(int length, int stringiness, int x, int y);
int goodforenergy(int x, int y, int poolsize);
int findcoord(int value, int move);
int replacenum(int replacee, int replacer);
int fattenone(int x, int y, int howfat, int middle, int ignorer, int replacer);
// howfat: positive 1-5 for distance, -100 to 0 for random 3 spread from 2 to 5.
int fattenall(int howfat, int middle, int ignorer, int replacer);
int findstartloc();
int whatheightstartloc(int x, int y);
int tileaverage(int x, int y, int threshold);
};
} // End of namespace Scumm
#endif // ENABLE_HE
#endif // SCUMM_HE_MOONBASE_MAP_KATTON_H

View File

@@ -0,0 +1,293 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/compression/deflate.h"
#include "common/config-manager.h"
#include "common/base64.h"
#include "common/memstream.h"
#include "common/bufferedstream.h"
#include "scumm/he/intern_he.h"
#include "scumm/he/moonbase/map_data.h"
#include "scumm/he/moonbase/map_main.h"
#include "scumm/he/moonbase/map_spiff.h"
#include "scumm/he/moonbase/map_katton.h"
namespace Scumm {
Map::Map(ScummEngine_v100he *vm) : _vm(vm), _rnd("moonbase") {
_mapGenerated = false;
_generatedMap = nullptr;
_generator = 0;
_size = 0;
_seed = 0;
_tileset = 0;
_energy = -1;
_terrain = -1;
_water = -1;
_encodedMap = Common::String();
}
Map::~Map() {
deleteMap();
}
bool Map::generateNewMap() {
deleteMap();
if (!ConfMan.getBool("generate_random_maps"))
return false;
// Show a dialog allowing the user to customize options.
if (!_vm->mapGeneratorDialog(!strcmp(_vm->_game.variant, "Demo")))
// They have clicked cancel, abort.
return false;
// Create a new seed just for the below values. This is to
// ensure these are truly random after generating a previous
// map (or to debug with a prefixed seed).
_rnd.generateNewSeed();
if (ConfMan.hasKey("map_size"))
_size = ConfMan.getInt("map_size");
if (_size < 4 || _size > 10)
// Don't randomly pick nonstandard map sizes.
_size = _rnd.getRandomNumberRngSigned(4, 8);
_size *= 8;
if (ConfMan.hasKey("map_algorithm"))
_generator = ConfMan.getInt("map_algorithm");
if (_generator < SPIFF_GEN || _generator > KATTON_GEN)
_generator = _rnd.getRandomNumberRng(1, 2);
if (ConfMan.hasKey("map_tileset"))
_tileset = ConfMan.getInt("map_tileset");
if (_tileset < 1 || _tileset > 6)
_tileset = _rnd.getRandomNumberRngSigned(1, 6);
if (!strcmp(_vm->_game.variant, "Demo") && (_tileset == 1 ||
_tileset == 3 || _tileset == 5)) {
// Demo version only has tilesets 1, 2, 4 and 6.
switch (_rnd.getRandomNumber(3)) {
case 0:
_tileset = 1;
break;
case 1:
_tileset = 2;
break;
case 2:
_tileset = 4;
break;
default:
_tileset = 6;
}
}
if (ConfMan.hasKey("map_energy"))
_energy = ConfMan.getInt("map_energy");
if (_energy < 0 || _energy > 6)
// Only use [2, 3, 4] of the legal [0, 1, 2, 3, 4, 5, 6]
_energy = _rnd.getRandomNumberRngSigned(2, 4);
if (ConfMan.hasKey("map_terrain"))
_terrain = ConfMan.getInt("map_terrain");
if (_terrain < 0 || _terrain > 6)
// Only use [2, 3, 4] of the legal [0, 1, 2, 3, 4, 5, 6]
_terrain = _rnd.getRandomNumberRngSigned(2, 4);
if (ConfMan.hasKey("map_water"))
_water = ConfMan.getInt("map_water");
if (_water < 0 || _water > 6)
// Only use [2, 3, 4] of the legal [0, 1, 2, 3, 4, 5, 6]
_water = _rnd.getRandomNumberRngSigned(2, 4);
// 32767 is RAND_MAX on Windows
int seed = _rnd.getRandomNumber(32767);
debug(1, "Map: Generating new map with info: generator = %d, seed = %d, size = %d, tileset = %d , energy = %d, terrain = %d, water = %d.", _generator, getSeed(), _size, _tileset, _energy, _terrain, _water);
switch (_generator) {
case SPIFF_GEN:
{
SpiffGenerator spiff = SpiffGenerator(seed);
_generatedMap = spiff.generateMap(_water, _tileset, _size, _energy, _terrain);
break;
}
case KATTON_GEN:
{
KattonGenerator katton = KattonGenerator(seed);
_generatedMap = katton.generateMap(_water, _tileset, _size, _energy, _terrain);
break;
}
default:
error("Map: Got unknown generator: %d", _generator);
return false;
}
// Encode the newly generated map file into base64 to transmit over the wire:
_encodedMap = Common::b64EncodeData(_generatedMap, sizeof(MapFile));
debug(2, "Map: Base64: %s", _encodedMap.c_str());
_mapGenerated = true;
return true;
}
bool Map::generateMapWithInfo(Common::String encodedMap, uint8 generator, int seed, int mapSize, int tileset, int energy, int terrain, int water) {
deleteMap();
_generator = generator;
_seed = seed;
_size = mapSize;
_tileset = tileset;
_energy = energy;
_terrain = terrain;
_water = water;
_encodedMap = encodedMap;
// Decode base64 encoded map file
debug(2, "Map: Generating map with base64: encodedMap: %s, generator = %d, seed = %d, mapSize = %d, tileset = %d , energy = %d, terrain = %d, water = %d.", encodedMap.c_str(), generator, getSeed(), mapSize, tileset, energy, terrain, water);
_generatedMap = new MapFile();
bool success = Common::b64DecodeData(encodedMap, _generatedMap);
if (!success) {
warning("Map: Error has occurred while decoding map data from base64");
return false;
}
_mapGenerated = true;
return true;
}
void Map::deleteMap() {
if (_mapGenerated) {
// Delete old map.
delete _generatedMap;
_generatedMap = nullptr;
_mapGenerated = false;
_generator = 0;
_size = 0;
_seed = 0;
_tileset = 0;
_energy = -1;
_terrain = -1;
_water = -1;
_encodedMap = "";
debug(1, "Map: Deleted.");
}
}
Common::SeekableReadStream *Map::makeWiz() {
unsigned short wiz [139][139];
int i, j;
Common::MemoryReadStream *wizTemplate = new Common::MemoryReadStream(Template_wiz, ARRAYSIZE(Template_wiz));
Common::SeekableReadStream *stream = Common::wrapCompressedReadStream(wizTemplate);
stream->seek(0x0448);
for (j = 0; j < 139; ++j) {
for (i = 0; i < 139; ++i) {
uint16 data = stream->readUint16LE();
wiz[i][j] = data;
}
}
delete stream;
for (j = 0; j < _energy * 9; j++) {
for (i = 30; i < 51; i++) {
wiz[i][91 - j] = ((255) / 8 ) + ((int) (130 - (j * 100 / (_energy * 9)))) / 8 * 32 + ((int) (80 + j * 80 / (_energy * 9)) / 8 * 1024);
}
}
for (j = 0; j < _terrain * 9; j++) {
for (i = 61; i < 82; i++) {
wiz[i][91 - j] = ((255) / 8 ) + ((int) (130 - (j * 100 / (_terrain*9))))/8 * 32 + ((int) (80 + j * 80 / (_terrain * 9)) / 8 * 1024);
}
}
for (j = 0; j < _water * 9; j++) {
for (i = 92; i < 113; i++) {
wiz[i][91 - j] = ((255) / 8 ) + ((int) (130 - (j * 100 / (_water * 9)))) / 8 * 32 + ((int) (80 + j * 80 / (_water*9)) / 8 * 1024);
}
}
// Re-read the template (to avoid compressed stream seeking):
wizTemplate = new Common::MemoryReadStream(Template_wiz, ARRAYSIZE(Template_wiz));
stream = Common::wrapCompressedReadStream(wizTemplate);
byte *pwiz = (byte *)malloc(TEMPLATE_WIZ_SIZE);
stream->read(pwiz, TEMPLATE_WIZ_SIZE);
delete stream;
Common::SeekableMemoryWriteStream ws = Common::SeekableMemoryWriteStream(pwiz, TEMPLATE_WIZ_SIZE);
ws.seek(0x0448);
for (j = 0; j < 139; j++) {
for (i = 0; i < 139; i++) {
ws.writeUint16LE(wiz[i][j]);
}
}
return new Common::MemoryReadStream(pwiz, TEMPLATE_WIZ_SIZE, DisposeAfterUse::YES);
}
Common::SeekableReadStream *Map::substituteFile(const byte *fileName) {
if (_mapGenerated) {
// Worth noting here that the game opens these files more than once.
// The exact scenario for the .thm and .wiz files is that the game
// opens it first to make sure that it exists and readable, closes it,
// and calls processWizImage to open the file again to actually read
// the data inside.
if (!strcmp((const char *)fileName, "map\\moon001.thm")) {
// Load compressed thumbnail data from header.
Common::MemoryReadStream *templateThm = new Common::MemoryReadStream(Template_thm, ARRAYSIZE(Template_thm));
Common::SeekableReadStream *stream = Common::wrapCompressedReadStream(templateThm);
// Read the uncompressed data into memory
// (This is done to avoid compressed stream seeking)
byte *thumbnail = (byte *)malloc(TEMPLATE_THM_SIZE);
stream->read(thumbnail, TEMPLATE_THM_SIZE);
delete stream;
// And return a new ReadStream for it.
return new Common::MemoryReadStream(thumbnail, TEMPLATE_THM_SIZE, DisposeAfterUse::YES);
}
if (!strcmp((const char *)fileName, "map\\moon001.wiz")) {
return makeWiz();
}
if (!strcmp((const char *)fileName, "map\\moon001.map") ||
!strcmp((const char *)fileName, "user\\Temp.map")) {
// (The Temp.map name is used when the game saves the map alongside
// replay data)
// Return new ReadStream but do not dispose it. We'll handle
// that ourselves.
return new Common::MemoryReadStream((byte *)_generatedMap, sizeof(MapFile));
}
}
return nullptr;
}
} // End of namespace Scumm

View File

@@ -0,0 +1,112 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_MAP_MAIN_H
#define SCUMM_HE_MOONBASE_MAP_MAIN_H
#ifdef ENABLE_HE
#include "common/random.h"
#include "common/stream.h"
#include "engines/scumm/he/moonbase/map_mif.h"
#define SPIFF_GEN 1
#define KATTON_GEN 2
namespace Scumm {
class Map {
public:
Map(ScummEngine_v100he *vm);
~Map();
bool generateNewMap();
bool generateMapWithInfo(Common::String encodedMap, uint8 generator, int seed, int mapSize, int tileset, int energy, int terrain, int water);
Common::SeekableReadStream *substituteFile(const byte *fileName);
void deleteMap();
uint8 getGenerator() const {
return _generator;
}
int getSize() const {
return _size;
}
int getSeed() const {
return _seed;
}
int getTileset() const {
return _tileset;
}
int getEnergy() const {
return _energy;
}
int getTerrain() const {
return _terrain;
}
int getWater() const {
return _water;
}
bool mapGenerated() const {
return _mapGenerated;
}
Common::String getEncodedMap() const {
return _encodedMap;
}
private:
ScummEngine_v100he *_vm;
// We require our own random number generator
// so we can send and set seeds from online players to ensure
// they're playing on the same generated map.
Common::RandomSource _rnd;
uint8 _generator;
int _size;
int _seed;
int _tileset;
int _energy;
int _terrain;
int _water;
bool _mapGenerated;
MapFile *_generatedMap;
Common::String _encodedMap;
Common::SeekableReadStream *makeWiz();
};
} // End of namespace Scumm
#endif // ENABLE_HE
#endif // SCUMM_HE_MOONBASE_MAP_MAIN_H

View File

@@ -0,0 +1,825 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/debug.h"
#include "common/str.h"
#include "common/textconsole.h"
#include "scumm/he/moonbase/map_mif.h"
namespace Scumm {
static const byte waterTileMap[] = {
0x44, 0x40, 0x32, 0x32, 0x3C, 0x38, 0x32, 0x32, // 0x00
0x2C, 0x2C, 0x26, 0x26, 0x2A, 0x2A, 0x26, 0x26,
0x2F, 0x2D, 0x27, 0x27, 0x2F, 0x2D, 0x27, 0x27, // 0x10
0x1B, 0x1B, 0x49, 0x49, 0x1B, 0x1B, 0x49, 0x49,
0x42, 0x3E, 0x30, 0x30, 0x3A, 0x36, 0x30, 0x30, // 0x20
0x2C, 0x2C, 0x26, 0x26, 0x2A, 0x2A, 0x26, 0x26,
0x2E, 0x22, 0x1E, 0x1E, 0x2E, 0x22, 0x1E, 0x1E, // 0x30
0x1B, 0x1B, 0x49, 0x49, 0x1B, 0x1B, 0x49, 0x49,
0x35, 0x33, 0x1C, 0x1C, 0x34, 0x24, 0x1C, 0x1C, // 0x40
0x28, 0x28, 0x4B, 0x4B, 0x1F, 0x1F, 0x4B, 0x4B,
0x29, 0x20, 0x4C, 0x4C, 0x29, 0x20, 0x4C, 0x4C, // 0x50
0x48, 0x48, 0x4A, 0x4A, 0x48, 0x48, 0x4A, 0x4A,
0x35, 0x33, 0x1C, 0x1C, 0x34, 0x24, 0x1C, 0x1C, // 0x60
0x28, 0x28, 0x4B, 0x4B, 0x1F, 0x1F, 0x4B, 0x4B,
0x29, 0x20, 0x4C, 0x4C, 0x29, 0x20, 0x4C, 0x4C, // 0x70
0x48, 0x48, 0x4A, 0x4A, 0x48, 0x48, 0x4A, 0x4A,
0x43, 0x3F, 0x31, 0x31, 0x3B, 0x37, 0x31, 0x31, // 0x80
0x2B, 0x2B, 0x1D, 0x1D, 0x21, 0x21, 0x1D, 0x1D,
0x2F, 0x2D, 0x27, 0x27, 0x2F, 0x2D, 0x27, 0x27, // 0x90
0x1B, 0x1B, 0x49, 0x49, 0x1B, 0x1B, 0x49, 0x49,
0x41, 0x3D, 0x23, 0x23, 0x39, 0x25, 0x23, 0x23, // 0xA0
0x2B, 0x2B, 0x1D, 0x1D, 0x21, 0x21, 0x1D, 0x1D,
0x2E, 0x22, 0x1E, 0x1E, 0x2E, 0x22, 0x1E, 0x1E, // 0xB0
0x1B, 0x1B, 0x49, 0x49, 0x1B, 0x1B, 0x49, 0x49,
0x35, 0x33, 0x1C, 0x1C, 0x34, 0x24, 0x1C, 0x1C, // 0xC0
0x28, 0x28, 0x4B, 0x4B, 0x1F, 0x1F, 0x4B, 0x4B,
0x29, 0x20, 0x4C, 0x4C, 0x29, 0x20, 0x4C, 0x4C, // 0xD0
0x48, 0x48, 0x4A, 0x4A, 0x48, 0x48, 0x4A, 0x4A,
0x35, 0x33, 0x1C, 0x1C, 0x34, 0x24, 0x1C, 0x1C, // 0xE0
0x28, 0x28, 0x4B, 0x4B, 0x1F, 0x1F, 0x4B, 0x4B,
0x29, 0x20, 0x4C, 0x4C, 0x29, 0x20, 0x4C, 0x4C, // 0xF0
0x48, 0x48, 0x4A, 0x4A, 0x48, 0x48, 0x4A, 0x4A
};
static int magic(int x, int y) {
static const byte matrix[8][8] = {
{ 2, 0, 2, 1, 3, 0, 3, 1 } ,
{ 3, 1, 0, 3, 2, 1, 0, 2 } ,
{ 0, 2, 1, 2, 0, 3, 1, 3 } ,
{ 1, 3, 0, 3, 1, 2, 0, 2 } ,
{ 2, 0, 1, 2, 3, 0, 1, 3 } ,
{ 3, 1, 3, 0, 2, 1, 2, 0 } ,
{ 0, 2, 0, 1, 3, 0, 3, 2 } ,
{ 1, 3, 0, 3, 2, 1, 2, 0 }
};
return matrix[y % 8][x % 8];
}
MIF::MIF() {
}
void MIF::generateMap(MapFile *map) {
map->terrainDimX = _dimension;
map->terrainDimY = _dimension;
map->mapType = _mapType;
Common::strlcpy(map->name, _name, 17);
int x, y;
for (y = 0; y < _dimension; ++y) {
for (x = 0; x < _dimension; ++x) {
map->terrainStates[x][y] = findTileFor(x, y);
}
}
defineEnergyPools(map);
defineStartLocations(map);
makeCraters(map);
}
void MIF::defineStartLocations(MapFile *map) {
int x, y;
for (y = 0; y < _dimension; ++y) {
for (x = 0; x < _dimension; ++x) {
int8 ch = _centerMap[x][y];
if (ch < 0) {
int i;
ch = -ch;
if (ch & 1) {
// 4 player start
i = 0;
while (i < 4) {
if (map->fourPlayerPoints[i].x == 0xFFFF) {
map->fourPlayerPoints[i].x = x * 60;
map->fourPlayerPoints[i].y = y * 60;
break;
}
++i;
}
}
ch = ch >> 1;
if (ch & 1) {
// 3 player start
i = 0;
while (i < 3) {
if (map->threePlayerPoints[i].x == 0xFFFF) {
map->threePlayerPoints[i].x = x * 60;
map->threePlayerPoints[i].y = y * 60;
break;
}
++i;
}
}
ch = ch >> 1;
if (ch & 1) {
// 2 player start
i = 0;
while (i < 2) {
if (map->twoPlayerPoints[i].x == 0xFFFF) {
map->twoPlayerPoints[i].x = x * 60;
map->twoPlayerPoints[i].y = y * 60;
break;
}
++i;
}
}
ch = ch >> 1;
if (ch & 1) {
// 2v2 player start
i = 0;
while (i < 4) {
if (map->twoVTwoPoints[i].x == 0xFFFF) {
map->twoVTwoPoints[i].x = x * 60;
map->twoVTwoPoints[i].y = y * 60;
break;
}
++i;
}
}
ch = ch >> 1;
if (ch & 1) {
// 1v3 player start
i = 0;
while (i < 4) {
if (map->oneVThreePoints[i].x == 0xFFFF) {
map->oneVThreePoints[i].x = x * 60;
map->oneVThreePoints[i].y = y * 60;
break;
}
++i;
}
}
ch = ch >> 1;
if (ch & 1) {
// 1v2 player start
i = 0;
while (i < 3) {
if (map->oneVTwoPoints[i].x == 0xFFFF) {
map->oneVTwoPoints[i].x = x * 60;
map->oneVTwoPoints[i].y = y * 60;
break;
}
++i;
}
}
}
}
}
}
void MIF::defineEnergyPools(MapFile *map) {
int x, y;
for (y = 0; y < _dimension; ++y) {
for (x = 0; x < _dimension; ++x) {
char ch = _centerMap[x][y];
if ('S' == ch || 'M' == ch || 'L' == ch) {
// Verify legal position
if (!((tlCorner(x, y) == trCorner(x, y)) && (blCorner(x, y) == brCorner(x, y)) &&
(tlCorner(x, y) == blCorner(x, y)) && (trCorner(x, y) == brCorner(x, y)))) {
error("small and medium energy pools must be on a flat tile (%d, %d)", x, y);
}
if ('L' == ch) {
byte nHeight;
nHeight = blCorner(x, y);
if (!(tlCorner(x, y) == nHeight && ttlCorner(x, y) == nHeight && ttrCorner(x, y) == nHeight && trCorner(x, y) == nHeight && brCorner(x, y) == nHeight)) {
error("large energy pools must be on the lower of two flat tiles (%d, %d)", x, y);
}
}
int xLoc;
int yLoc;
if ('S' == ch) {
xLoc = 60 * x + 30 + 20000;
yLoc = 60 * y + 30;
} else if ('M' == ch) {
xLoc = 60 * x + 30 + 10000;
yLoc = 60 * y + 30;
} else {
xLoc = 60 * x + 30;
yLoc = 60 * y;
}
if (map->numEnergyPools < 49) {
map->poolLocs[map->numEnergyPools].location.x = xLoc;
map->poolLocs[map->numEnergyPools].location.y = yLoc;
++map->numEnergyPools;
} else if (map->numEnergyPools == 49) {
map->lastPool.x = xLoc;
map->lastPool.y = yLoc;
++map->numEnergyPools;
} else {
error("only 50 energy pools are allowed, this is the 51st (%d, %d)", x, y);
}
}
}
}
}
void MIF::makeCraters(MapFile *map) {
// squarenumber, type, x, y (offset from top left (abs y)). x/y = 9 if none of that type
static const byte locations[8][3][2] = {
{ {1, 1}, {5, 2}, {3, 5} },
{ {6, 1}, {1, 6}, {2, 0} },
{ {0, 4}, {3, 2}, {6, 5} },
{ {4, 4}, {5, 0}, {9, 9} },
{ {3, 6}, {9, 9}, {2, 1} },
{ {9, 9}, {3, 3}, {0, 2} },
{ {2, 4}, {0, 0}, {5, 3} },
{ {4, 1}, {0, 3}, {5, 6} }
};
// I made up the crater patterns for sizes larger than SAI
// This will work for maps up to 80x80
static byte const largegrid[10][10] = {
{0, 1, 2, 3, 4, 5, 6, 7, 0, 1},
{2, 3, 4, 5, 6, 7, 0, 1, 2, 3},
{4, 5, 6, 7, 0, 1, 2, 3, 4, 5},
{3, 0, 1, 2, 6, 4, 5, 7, 3, 0},
{1, 2, 3, 4, 5, 6, 7, 0, 1, 2},
{3, 4, 5, 6, 7, 0, 1, 2, 3, 4},
{6, 3, 0, 1, 2, 7, 4, 5, 6, 3},
{5, 6, 7, 0, 1, 2, 3, 4, 5, 6},
{0, 1, 2, 3, 4, 5, 6, 7, 0, 1},
{2, 3, 4, 5, 6, 7, 0, 1, 2, 3}
};
for (int i = 0; i < _dimension / 8; i++) {
for (int j = 0; j < _dimension / 8; j++) {
for (int nCrater = 0; nCrater < 3; nCrater++) {
if (9 == locations[largegrid[j][i]][nCrater][0]) {
continue;
}
int x = locations[largegrid[j][i]][nCrater][0] + i * 8;
int y = locations[largegrid[j][i]][nCrater][1] + j * 8;
byte nLevel = tlCorner(x, y);
if ((tlCorner(x, y) == nLevel) && (trCorner(x, y) == nLevel) && (trrCorner(x, y) == nLevel) &&
(_centerMap[x][y] != 'W') && (_centerMap[x + 1][y] != 'W') &&
(blCorner(x, y) == nLevel) && (brCorner(x, y) == nLevel) && (brrCorner(x, y) == nLevel) &&
(_centerMap[x][y + 1] != 'W') && (_centerMap[x + 1][y + 1] != 'W') &&
(bblCorner(x, y) == nLevel) && (bbrCorner(x, y) == nLevel) && (bbrrCorner(x, y) == nLevel)) {
// The tile values follow a predictable pattern, level one craters in order, etc.
int16 nBase = 0xA6 + (tlCorner(x, y) * 12) + (nCrater * 4);
map->terrainStates[x][y] = nBase;
map->terrainStates[x + 1][y] = nBase + 1;
map->terrainStates[x][y + 1] = nBase + 2;
map->terrainStates[x + 1][y + 1] = nBase + 3;
}
}
}
}
}
uint16 MIF::findTileFor(int x, int y) {
int index;
int8 ch;
byte aLowBlanks[] = {0x93, 0x94, 0x00, 0x96};
byte aMedBlanks[] = {0x97, 0x99, 0x0D, 0x9A};
byte aHiBlanks[] = {0x9B, 0x9C, 0x1A, 0x9D};
ch = _centerMap[x][y];
debug(5, "MIF: Tile for %d, %d is %c", x, y, ch);
if ('S' == ch || 'M' == ch || 'L' == ch || '.' == ch || ch < 0) {
// Do the easy cases, things with no transitions.
if (0 == tlCorner(x, y) && 0 == trCorner(x, y) && 0 == blCorner(x, y) && 0 == brCorner(x, y))
return aLowBlanks[magic(x, y)];
if (1 == tlCorner(x, y) && 1 == trCorner(x, y) && 1 == blCorner(x, y) && 1 == brCorner(x, y))
return aMedBlanks[magic(x, y)];
if (2 == tlCorner(x, y) && 2 == trCorner(x, y) && 2 == blCorner(x, y) && 2 == brCorner(x, y))
return aHiBlanks[magic(x, y)];
//
// Low to med transitions
//
if (0 == tlCorner(x, y) || 0 == trCorner(x, y) || 0 == blCorner(x, y) || 0 == brCorner(x, y)) {
// Corner cases
int cornerSum = tlCorner(x, y) + trCorner(x, y) + blCorner(x, y) + brCorner(x, y);
if (1 == cornerSum) {
if (tlCorner(x, y)) {
if (tllCorner(x, y) > 0 && ttlCorner(x, y) > 0)
return 0x03;
else
return 0x89;
} else if (trCorner(x, y)) {
if (trrCorner(x, y) > 0 && ttrCorner(x, y) > 0)
return 0x04;
else
return 0x8C;
} else if (blCorner(x, y)) {
if (bllCorner(x, y) > 0 && bblCorner(x, y) > 0)
return 0x02;
else
return 0x86;
} else // brCorner
{
if (brrCorner(x, y) > 0 && bbrCorner(x, y) > 0)
return 0x01;
else
return 0x83;
}
}
// Straight edges
// edge on bottom
if (tlCorner(x, y) == 0 && trCorner(x, y) == 0 && blCorner(x, y) == 1 && brCorner(x, y) == 1) {
bool bLeftEased = (bllCorner(x, y) == 0 && bblCorner(x, y) == 1);
bool bRightEased = (brrCorner(x, y) == 0 && bbrCorner(x, y) == 1);
if (bLeftEased && bRightEased)
return 0x0A;
if (!bLeftEased && bRightEased)
return 0x54;
if (bLeftEased && !bRightEased)
return 0x55;
if (!bLeftEased && !bRightEased)
return (magic(x, y) & 0x01) ? 0x9F : 0x56;
}
// edge on top
else if (tlCorner(x, y) == 1 && trCorner(x, y) == 1 && blCorner(x, y) == 0 && brCorner(x, y) == 0) {
bool bLeftEased = (tllCorner(x, y) == 0 && ttlCorner(x, y) == 1);
bool bRightEased = (trrCorner(x, y) == 0 && ttrCorner(x, y) == 1);
if (bLeftEased && bRightEased)
return 0x0C;
if (!bLeftEased && bRightEased)
return 0x52;
if (bLeftEased && !bRightEased)
return 0x51;
if (!bLeftEased && !bRightEased)
return (magic(x, y) & 0x01) ? 0xA1 : 0x53;
}
// edge on right
if (tlCorner(x, y) == 0 && blCorner(x, y) == 0 && trCorner(x, y) == 1 && brCorner(x, y) == 1) {
bool bTopEased = (ttrCorner(x, y) == 0 && trrCorner(x, y) == 1);
bool bBotEased = (bbrCorner(x, y) == 0 && brrCorner(x, y) == 1);
if (bTopEased && bBotEased)
return 0x09;
if (!bTopEased && bBotEased)
return 0x5B;
if (bTopEased && !bBotEased)
return 0x5A;
if (!bTopEased && !bBotEased)
return (magic(x, y) & 0x01) ? 0x9E : 0x5C;
}
// edge on left
if (tlCorner(x, y) == 1 && blCorner(x, y) == 1 && trCorner(x, y) == 0 && brCorner(x, y) == 0) {
bool bTopEased = (ttlCorner(x, y) == 0 && tllCorner(x, y) == 1);
bool bBotEased = (bblCorner(x, y) == 0 && bllCorner(x, y) == 1);
if (bTopEased && bBotEased)
return 0x0B;
if (!bTopEased && bBotEased)
return 0x57;
if (bTopEased && !bBotEased)
return 0x58;
if (!bTopEased && !bBotEased)
return (magic(x, y) & 0x01) ? 0xA0 : 0x59;
}
// Three corner cases
// 0 1 1 1 0 0 0 0 0 0
// 0 1 1 0x5F 0 1 1 0x5E 0 1 1 0x62 0 1 1 0x60
// 0 0 0 0 0 0 0 1 1 1
//
// 0 1 1 1 0 0 1 0 0 1
// 0 1 1 0x5D 0 1 1 0x06 0 1 1 0x61 0 1 1 0x07
// 0 0 1 0 0 1 0 1 1 1
//
//
// 0 0 0 1 0 0 1 0 1 1
// 1 1 0 0x65 1 1 0 0x63 1 1 0 0x68 1 1 0 0x66
// 1 0 1 0 0 0 0 0 0 0
//
// 0 0 0 1 0 0 1 0 1 1
// 1 1 0 0x64 1 1 0 0x08 1 1 0 0x67 1 1 0 0x05
// 1 1 1 1 1 0 0 1 0 0
// corner in upper left
if (blCorner(x, y) == 1 && tlCorner(x, y) == 1 && trCorner(x, y) == 1) {
bool BLDiag = (bllCorner(x, y) > 0 && bblCorner(x, y) == 0);
bool TRDiag = (ttrCorner(x, y) > 0 && trrCorner(x, y) == 0);
if (!BLDiag && !TRDiag)
return 0x62;
else if (!BLDiag && TRDiag)
return 0x61;
else if (BLDiag && !TRDiag)
return 0x60;
else
return 0x07;
}
// corner in upper right
if (tlCorner(x, y) == 1 && trCorner(x, y) == 1 && brCorner(x, y) == 1) {
bool TLDiag = (ttlCorner(x, y) > 0 && tllCorner(x, y) == 0);
bool BRDiag = (brrCorner(x, y) > 0 && bbrCorner(x, y) == 0);
if (!TLDiag && !BRDiag)
return 0x65;
else if (!TLDiag && BRDiag)
return 0x64;
else if (TLDiag && !BRDiag)
return 0x63;
else
return 0x08;
}
// corner in bottom right
if (trCorner(x, y) == 1 && brCorner(x, y) == 1 && blCorner(x, y) == 1) {
bool TRDiag = (trrCorner(x, y) > 0 && ttrCorner(x, y) == 0);
bool BLDiag = (bblCorner(x, y) > 0 && bllCorner(x, y) == 0);
if (!TRDiag && !BLDiag)
return 0x68;
else if (!TRDiag && BLDiag)
return 0x67;
else if (TRDiag && !BLDiag)
return 0x66;
else
return 0x05;
}
// corner in bottom left
if (brCorner(x, y) == 1 && blCorner(x, y) == 1 && tlCorner(x, y) == 1) {
bool TLDiag = (tllCorner(x, y) > 0 && ttlCorner(x, y) == 0);
bool BRDiag = (bbrCorner(x, y) > 0 && brrCorner(x, y) == 0);
if (!TLDiag && !BRDiag)
return 0x5F;
else if (!TLDiag && BRDiag)
return 0x5D;
else if (TLDiag && !BRDiag)
return 0x5E;
else
return 0x06;
}
// Opposing corner cases
if (tlCorner(x, y) == 1 && brCorner(x, y) == 1) {
// There are four cases, big big, big small, small big, small small
// big big
if (tllCorner(x, y) > 0 && ttlCorner(x, y) > 0 && brrCorner(x, y) > 0 && bbrCorner(x, y) > 0)
return 0x4D;
// big small
if (tllCorner(x, y) > 0 && ttlCorner(x, y) > 0)
return 0x81;
// small big
if (brrCorner(x, y) > 0 && bbrCorner(x, y) > 0)
return 0x82;
// small small
return 0x84;
}
if (trCorner(x, y) == 1 && blCorner(x, y) == 1) {
// There are four cases, big big, big small, small big, small small
// big big
if (trrCorner(x, y) > 0 && ttrCorner(x, y) > 0 && bllCorner(x, y) > 0 && bblCorner(x, y) > 0)
return 0x4E;
// big small
if (trrCorner(x, y) > 0 && ttrCorner(x, y) > 0)
return 0x85;
// small big
if (bllCorner(x, y) > 0 && bblCorner(x, y) > 0)
return 0x87;
// small small
return 0x88;
}
}
//
// Med to high transitions
//
if (1 == tlCorner(x, y) || 1 == trCorner(x, y) || 1 == blCorner(x, y) || 1 == brCorner(x, y)) {
// Corner cases
int cornerSum = (tlCorner(x, y) == 2) + (trCorner(x, y) == 2) + (blCorner(x, y) == 2) + (brCorner(x, y) == 2);
if (1 == cornerSum) {
if (tlCorner(x, y) == 2) {
if (tllCorner(x, y) == 2 && ttlCorner(x, y) == 2)
return 0x10;
else
return 0x95;
} else if (trCorner(x, y) == 2) {
if (trrCorner(x, y) == 2 && ttrCorner(x, y) == 2)
return 0x11;
else
return 0x98;
} else if (blCorner(x, y) == 2) {
if (bllCorner(x, y) == 2 && bblCorner(x, y) == 2)
return 0x0F;
else
return 0x92;
} else // brCorner
{
if (brrCorner(x, y) == 2 && bbrCorner(x, y) == 2)
return 0x0E;
else
return 0x8F;
}
}
// Straight edges
// edge on bottom
if (tlCorner(x, y) < 2 && trCorner(x, y) < 2 && blCorner(x, y) == 2 && brCorner(x, y) == 2) {
bool bLeftEased = (bllCorner(x, y) < 2 && bblCorner(x, y) == 2);
bool bRightEased = (brrCorner(x, y) < 2 && bbrCorner(x, y) == 2);
if (bLeftEased && bRightEased)
return 0x17;
if (!bLeftEased && bRightEased)
return 0x6C;
if (bLeftEased && !bRightEased)
return 0x6D;
if (!bLeftEased && !bRightEased)
return (magic(x, y) & 0x01) ? 0xA3 : 0x6E;
}
// edge on top
else if (tlCorner(x, y) == 2 && trCorner(x, y) == 2 && blCorner(x, y) < 2 && brCorner(x, y) < 2) {
bool bLeftEased = (tllCorner(x, y) < 2 && ttlCorner(x, y) == 2);
bool bRightEased = (trrCorner(x, y) < 2 && ttrCorner(x, y) == 2);
if (bLeftEased && bRightEased)
return 0x19;
if (!bLeftEased && bRightEased)
return 0x6A;
if (bLeftEased && !bRightEased)
return 0x69;
if (!bLeftEased && !bRightEased)
return (magic(x, y) & 0x01) ? 0xA5 : 0x6B;
}
// edge on right
if (tlCorner(x, y) < 2 && blCorner(x, y) < 2 && trCorner(x, y) == 2 && brCorner(x, y) == 2) {
bool bTopEased = (ttrCorner(x, y) < 2 && trrCorner(x, y) == 2);
bool bBotEased = (bbrCorner(x, y) < 2 && brrCorner(x, y) == 2);
if (bTopEased && bBotEased)
return 0x16;
if (!bTopEased && bBotEased)
return 0x73;
if (bTopEased && !bBotEased)
return 0x72;
if (!bTopEased && !bBotEased)
return (magic(x, y) & 0x01) ? 0xA2 : 0x74;
}
// edge on left
if (tlCorner(x, y) == 2 && blCorner(x, y) == 2 && trCorner(x, y) < 2 && brCorner(x, y) < 2) {
bool bTopEased = (ttlCorner(x, y) < 2 && tllCorner(x, y) == 2);
bool bBotEased = (bblCorner(x, y) < 2 && bllCorner(x, y) == 2);
if (bTopEased && bBotEased)
return 0x18;
if (!bTopEased && bBotEased)
return 0x6F;
if (bTopEased && !bBotEased)
return 0x70;
if (!bTopEased && !bBotEased)
return (magic(x, y) & 0x01) ? 0xA4 : 0x71;
}
// edge on bottom
if (tlCorner(x, y) == 1 && trCorner(x, y) == 1 && blCorner(x, y) == 2 && brCorner(x, y) == 2) {
// no other high corners
if (bllCorner(x, y) < 2 && brrCorner(x, y) < 2)
return 0x17;
// high corner on left
if (bllCorner(x, y) == 2 && brrCorner(x, y) < 2)
return 0x6C;
// high corner on right
if (bllCorner(x, y) < 2 && brrCorner(x, y) == 2)
return 0x6D;
// both neighbor corners high
if (bllCorner(x, y) == 2 && brrCorner(x, y) == 2)
return (magic(x, y) & 0x01) ? 0xA3 : 0x6E;
}
// edge on top
else if (tlCorner(x, y) == 2 && trCorner(x, y) == 2 && blCorner(x, y) == 1 && brCorner(x, y) == 1) {
// no other high corners
if (tllCorner(x, y) < 2 && trrCorner(x, y) < 2)
return 0x19;
// high corner on left
if (tllCorner(x, y) == 2 && trrCorner(x, y) < 2)
return 0x6A;
// high corner on right
if (tllCorner(x, y) < 2 && trrCorner(x, y) == 2)
return 0x69;
// both neighbor corners high
if (tllCorner(x, y) == 2 && trrCorner(x, y) == 2)
return (magic(x, y) & 0x01) ? 0xA5 : 0x6B;
}
// edge on right
if (tlCorner(x, y) == 1 && blCorner(x, y) == 1 && trCorner(x, y) == 2 && brCorner(x, y) == 2) {
// no high neighbor corners
if (ttrCorner(x, y) < 2 && bbrCorner(x, y) < 2)
return 0x16;
// high neighbor corner on top
if (ttrCorner(x, y) == 2 && bbrCorner(x, y) < 2)
return 0x73;
// high neighbor corner on bottom
if (ttrCorner(x, y) < 2 && bbrCorner(x, y) == 2)
return 0x72;
// both neighbor corners high
if (ttrCorner(x, y) == 2 && bbrCorner(x, y) == 2)
return (magic(x, y) & 0x01) ? 0xA2 : 0x74;
}
// edge on left
if (tlCorner(x, y) == 2 && blCorner(x, y) == 2 && trCorner(x, y) == 1 && brCorner(x, y) == 1) {
// no high neighbor corners
if (ttlCorner(x, y) < 2 && bblCorner(x, y) < 2)
return 0x18;
// high neighbor corner on top
if (ttlCorner(x, y) == 2 && bblCorner(x, y) < 2)
return 0x6F;
// high neighbor corner on bottom
if (ttlCorner(x, y) < 2 && bblCorner(x, y) == 2)
return 0x70;
// both neighbor corners high
if (ttlCorner(x, y) == 2 && bblCorner(x, y) == 2)
return (magic(x, y) & 0x01) ? 0xA4 : 0x71;
}
// Three corner cases
// Three corner cases
// 0 1 1 1 0 0 0 0 0 0
// 0 1 1 0x77 0 1 1 0x76 0 1 1 0x7A 0 1 1 0x78
// 0 0 0 0 0 0 0 1 1 1
//
// 0 1 1 1 0 0 1 0 0 1
// 0 1 1 0x75 0 1 1 0x13 0 1 1 0x79 0 1 1 0x14
// 0 0 1 0 0 1 0 1 1 1
//
//
// 0 0 0 1 0 0 1 0 1 1
// 1 1 0 0x7D 1 1 0 0x7B 1 1 0 0x80 1 1 0 0x7E
// 1 0 1 0 0 0 0 0 0 0
//
// 0 0 0 1 0 0 1 0 1 1
// 1 1 0 0x7C 1 1 0 0x15 1 1 0 0x7F 1 1 0 0x12
// 1 1 1 1 1 0 0 1 0 0
// corner in upper left
if (blCorner(x, y) == 2 && tlCorner(x, y) == 2 && trCorner(x, y) == 2) {
bool BLDiag = (bllCorner(x, y) > 1 && bblCorner(x, y) < 2);
bool TRDiag = (ttrCorner(x, y) > 1 && trrCorner(x, y) < 2);
if (!BLDiag && !TRDiag)
return 0x7A;
else if (!BLDiag && TRDiag)
return 0x79;
else if (BLDiag && !TRDiag)
return 0x78;
else
return 0x14;
}
// corner in upper right
if (tlCorner(x, y) == 2 && trCorner(x, y) == 2 && brCorner(x, y) == 2) {
bool TLDiag = ((ttlCorner(x, y) > 1) && (tllCorner(x, y) < 2));
bool BRDiag = ((brrCorner(x, y) > 1) && (bbrCorner(x, y) < 2));
if (!TLDiag && !BRDiag)
return 0x7D;
else if (!TLDiag && BRDiag)
return 0x7C;
else if (TLDiag && !BRDiag)
return 0x7B;
else
return 0x15;
}
// corner in bottom right
if (trCorner(x, y) == 2 && brCorner(x, y) == 2 && blCorner(x, y) == 2) {
bool TRDiag = (trrCorner(x, y) > 1 && ttrCorner(x, y) < 2);
bool BLDiag = (bblCorner(x, y) > 1 && bllCorner(x, y) < 2);
if (!TRDiag && !BLDiag)
return 0x80;
else if (!TRDiag && BLDiag)
return 0x7F;
else if (TRDiag && !BLDiag)
return 0x7E;
else
return 0x12;
}
// corner in bottom left
if (brCorner(x, y) == 2 && blCorner(x, y) == 2 && tlCorner(x, y) == 2) {
bool TLDiag = (tllCorner(x, y) > 1 && ttlCorner(x, y) < 2);
bool BRDiag = (bbrCorner(x, y) > 1 && brrCorner(x, y) < 2);
if (!TLDiag && !BRDiag)
return 0x77;
else if (!TLDiag && BRDiag)
return 0x75;
else if (TLDiag && !BRDiag)
return 0x76;
else
return 0x13;
}
// Opposing corner cases
if (tlCorner(x, y) == 2 && brCorner(x, y) == 2) {
// There are four cases, big big, big small, small big, small small
// big big
if (tllCorner(x, y) == 2 && ttlCorner(x, y) == 2 && brrCorner(x, y) == 2 && bbrCorner(x, y) == 2)
return 0x4F;
// big small
if (tllCorner(x, y) == 2 && ttlCorner(x, y) == 2)
return 0x8A;
// small big
if (brrCorner(x, y) == 2 && bbrCorner(x, y) == 2)
return 0x8B;
// small small
return 0x8D;
}
if (trCorner(x, y) == 2 && blCorner(x, y) == 2) {
// There are four cases, big big, big small, small big, small small
// big big
if (trrCorner(x, y) == 2 && ttrCorner(x, y) == 2 && bllCorner(x, y) == 2 && bblCorner(x, y) == 2)
return 0x50;
// big small
if (trrCorner(x, y) == 2 && ttrCorner(x, y) == 2)
return 0x8E;
// small big
if (bllCorner(x, y) == 2 && bblCorner(x, y) == 2)
return 0x90;
// small small
return 0x91;
}
}
error("illegal corner height arrangement (%d, %d)", x, y);
} else if ('W' == ch) {
// Check to make sure that we're on ground level
if (tlCorner(x, y) > 0 || trCorner(x, y) > 0 || blCorner(x, y) > 0 || brCorner(x, y) > 0)
error("water must be on a flat tile (%d, %d)", x, y);
index = (('W' != tlCenter(x, y)) << 7) |
(('W' != tCenter(x, y)) << 6) |
(('W' != trCenter(x, y)) << 5) |
(('W' != lCenter(x, y)) << 4) |
(('W' != rCenter(x, y)) << 3) |
(('W' != blCenter(x, y)) << 2) |
(('W' != bCenter(x, y)) << 1) |
('W' != brCenter(x, y));
uint16 nWaterTile = waterTileMap[index];
if (0x44 == nWaterTile) {
uint16 aWaterBlanks[] = {0x45, 0x46, 0x44, 0x47};
nWaterTile = aWaterBlanks[magic(x, y)];
}
return nWaterTile;
} else
error("illegal tile character (%d, %d)", x, y);
error("unknown tile find error (%d, %d)", x, y);
}
} // End of namespace Scumm

View File

@@ -0,0 +1,203 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_MAP_MIF_H
#define SCUMM_HE_MOONBASE_MAP_MIF_H
#ifdef ENABLE_HE
#include "scumm/he/intern_he.h"
namespace Scumm {
#define MAX_TILE_COUNT 80
#include "common/pack-start.h" // START STRUCT PACKING
struct PixelLoc {
uint16 x;
uint16 y;
};
struct EnergyPoolLoc {
PixelLoc location;
PixelLoc dummy;
};
struct MapFile {
// Header:
uint16 headerDummy;
uint16 terrainDimX;
uint16 terrainDimY;
uint16 mapType;
uint16 numEnergyPools;
uint16 terrainStates[80][161];
uint8 space1[230];
char name[17];
uint8 space2[25837];
EnergyPoolLoc poolLocs[49];
PixelLoc lastPool;
uint16 dummy;
PixelLoc fourPlayerPoints[4]; // 2^0
PixelLoc threePlayerPoints[3]; // 2^1
PixelLoc twoPlayerPoints[2]; // 2^2
PixelLoc twoVTwoPoints[4]; // 2^3
PixelLoc oneVThreePoints[4]; // 2^4
PixelLoc oneVTwoPoints[3]; // 2^5
MapFile() {
memset(this, 0, sizeof(MapFile));
mapType = 1;
memset(fourPlayerPoints, 0xFF, sizeof(fourPlayerPoints));
memset(threePlayerPoints, 0xFF, sizeof(threePlayerPoints));
memset(twoPlayerPoints, 0xFF, sizeof(twoPlayerPoints));
memset(twoVTwoPoints, 0xFF, sizeof(twoVTwoPoints));
memset(oneVThreePoints, 0xFF, sizeof(oneVThreePoints));
memset(oneVTwoPoints, 0xFF, sizeof(oneVTwoPoints));
}
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
class MIF {
public:
MIF();
void generateMap(MapFile *map);
int _dimension = 0; // 32 (small), 40 (medium), 48 (large), 56 (huge), 64 (SAI)
int _mapType = 0;
char _name[17] = {};
byte _cornerMap[MAX_TILE_COUNT][MAX_TILE_COUNT] = { {}, {} };
int8 _centerMap[MAX_TILE_COUNT][MAX_TILE_COUNT] = { {}, {} };
private:
void defineStartLocations(MapFile *map);
void defineEnergyPools(MapFile *map);
void makeCraters(MapFile *map);
uint16 findTileFor(int x, int y);
inline char tlCenter(int x, int y) const {
return _centerMap[(0 == x) ? _dimension - 1 : x - 1][(0 == y) ? _dimension - 1 : y - 1];
}
inline char tCenter(int x, int y) const {
return _centerMap[x][(0 == y) ? _dimension - 1 : y - 1];
}
inline char trCenter(int x, int y) const {
return _centerMap[(x + 1) % _dimension][(0 == y) ? _dimension - 1 : y - 1];
}
inline char lCenter(int x, int y) const {
return _centerMap[(0 == x) ? _dimension - 1 : x - 1][y];
}
inline char rCenter(int x, int y) const {
return _centerMap[(x + 1) % _dimension][y];
}
inline char blCenter(int x, int y) const {
return _centerMap[(0 == x) ? _dimension - 1 : x - 1][(y + 1) % _dimension];
}
inline char bCenter(int x, int y) const {
return _centerMap[x][(y + 1) % _dimension];
}
inline char brCenter(int x, int y) const {
return _centerMap[(x + 1) % _dimension][(y + 1) % _dimension];
}
inline byte tlCorner(int x, int y) const {
return _cornerMap[x][y];
}
inline byte trCorner(int x, int y) const {
return _cornerMap[(x + 1) % _dimension][y];
}
inline byte blCorner(int x, int y) const {
return _cornerMap[x][(y + 1) % _dimension];
}
inline byte brCorner(int x, int y) const {
return _cornerMap[(x + 1) % _dimension][(y + 1) % _dimension];
}
inline byte ttllCorner(int x, int y) const {
return tlCorner((x == 0) ? _dimension - 1 : x - 1, (y == 0) ? _dimension - 1: y - 1);
}
inline byte ttlCorner(int x, int y) const {
return trCorner((x == 0) ? _dimension - 1 : x - 1, (y == 0) ? _dimension - 1: y - 1);
}
inline byte ttrCorner(int x, int y) const {
return tlCorner((x + 1) % _dimension, (y == 0) ? _dimension - 1: y - 1);
}
inline byte ttrrCorner(int x, int y) const {
return trCorner((x + 1) % _dimension, (y == 0) ? _dimension - 1: y - 1);
}
inline byte tllCorner(int x, int y) const {
return tlCorner((x == 0) ? _dimension - 1 : x - 1, y);
}
inline byte trrCorner(int x, int y) const {
return trCorner((x + 1) % _dimension, y);
}
inline byte bllCorner(int x, int y) const {
return blCorner((x == 0) ? _dimension - 1 : x - 1, y);
}
inline byte brrCorner(int x, int y) const {
return brCorner((x + 1) % _dimension, y);
}
inline byte bbllCorner(int x, int y) const {
return blCorner((x == 0) ? _dimension - 1 : x - 1, (y + 1) % _dimension);
}
inline byte bblCorner(int x, int y) const {
return brCorner((x == 0) ? _dimension - 1 : x - 1, (y + 1) % _dimension);
}
inline byte bbrCorner(int x, int y) const {
return blCorner((x + 1) % _dimension, (y + 1) % _dimension);
}
inline byte bbrrCorner(int x, int y) const {
return brCorner((x + 1) % _dimension, (y + 1) % _dimension);
}
};
} // End of namespace Scumm
#endif // ENABLE_HE
#endif // SCUMM_HE_MOONBASE_MAP_MIF_H

View File

@@ -0,0 +1,620 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/debug.h"
#include "scumm/he/moonbase/map_spiff.h"
namespace Scumm {
SpiffGenerator::SpiffGenerator(int seed) {
_seed = seed;
}
MapFile *SpiffGenerator::generateMap(int water, int tileset, int mapSize, int energy, int terrain) {
_totalMapSizeG = mapSize;
_energyAmountG = (2 + energy) * _totalMapSizeG * _totalMapSizeG;
_islandsFlagG = pickFrom2(0, 1, water - 4, (water >= 5)); // 1 is large islands, 2 is small
if (_islandsFlagG) {
water -= 3;
_energyAmountG = (int)(_energyAmountG * (5 - _islandsFlagG) / 6); // *2/3 or *1/2
}
_waterAmountG = 4 * water;
_cliffAmountG = 1 << terrain;
_advancedMirrorOK_G = ((terrain > 1) && (water < 6)) || _islandsFlagG;
_terrainSeedFlagG = 2 * water - terrain;
int n = (int)(_energyAmountG / 2700);
if (n > 12) {
n = 12;
}
if (n < 1) {
n = 1;
}
_numPoolsG = spiffRand((int)(_energyAmountG / 4000) + 1, n);
if (_numPoolsG > 12) {
_numPoolsG = 12;
}
generate();
// Populate MIF for map data generation:
MIF mif = MIF();
int levelMap[MAXELEVVAL];
levelMap[kElevHigh] = 2;
levelMap[kElevMedium] = 1;
levelMap[kElevLow] = 0;
mif._mapType = tileset;
Common::sprintf_s(mif._name, "Spiff %04X", (uint16)_seed);
mif._dimension = _totalMapSizeG;
int y;
int x;
byte t;
int XOffset = spiffRand(0, _totalMapSizeG-1);
int YOffset = spiffRand(0, _totalMapSizeG-1);
int newX;
int newY;
for (y = 0, newY = YOffset; y < _totalMapSizeG; ++y, ++newY) {
for (x = 0, newX = XOffset; x < _totalMapSizeG; ++x, ++newX) {
if (newX == _totalMapSizeG)
newX = 0;
if (newY == _totalMapSizeG)
newY = 0;
mif._cornerMap[newX][newY] = levelMap[_mapCorner[x][y]];
switch (_mapMiddle[x][y]) {
case HUB:
t = 0xff;
break;
case SMALLPOOL:
t = 'S';
break;
case MEDIUMPOOL:
t = 'M';
break;
case LARGEPOOLBOTTOM:
t = 'L';
break;
case WATER:
t = 'W';
break;
case UNASSIGNED:
case LARGEPOOLTOP:
t = '.';
break;
default:
t = '?';
}
mif._centerMap[newX][newY] = t;
}
}
// Generate new map:
MapFile *map = new MapFile();
mif.generateMap(map);
return map;
}
float SpiffGenerator::getRandomFloat() {
// This is the exact linear congruential generator
// algorithm used on MSVCRT (Windows Visual C++ Runtime), with
// RAND_MAX being 0x7fff (32767). This is implemented here
// to match the RNG between the original Moonbase Console
// program and ScummVM.
//
// (Common::RandomSource uses Xorshift and uses unsigned
// integers compared to MSVCRT's rand)
_seed = _seed * 214013 + 2531011;
return (float)((_seed >> 16) & 0x7fff) / 32767;
}
int SpiffGenerator::spiffRand(int min, int max) {
// returns a random integer min to max inclusive
return ((int)(getRandomFloat() * (max + 1 - min))) + min;
}
int SpiffGenerator::pickFrom2(int a, int probA, int b, int probB) {
debug(3, "SpiffGenerator::pickFrom2(%d, %d, %d, %d)", a, probA, b, probB);
float r = getRandomFloat() * (probA + probB);
debug(3, " r = %f", r);
if (r < probA)
return a;
else
return b;
}
int SpiffGenerator::pickFrom3(int a, int probA, int b, int probB, int c, int probC) {
debug(3, "SpiffGenerator::pickFrom3(%d, %d, %d, %d, %d, %d)", a, probA, b, probB, c, probC);
float r = getRandomFloat() * (probA + probB + probC);
debug(3, " r = %f", r);
if (r < probA)
return a;
else if (r < probA + probB)
return b;
else
return c;
}
int SpiffGenerator::pickFrom4(int a, int probA, int b, int probB, int c, int probC, int d, int probD) {
debug(3, "SpiffGenerator::pickFrom4(%d, %d, %d, %d, %d, %d, %d, %d)", a, probA, b, probB, c, probC, d, probD);
float r = getRandomFloat() * (probA + probB + probC + probD);
debug(3, " r = %f", r);
if (r < probA)
return a;
else if (r < probA + probB)
return b;
else if (r < probA + probB + probC)
return c;
else
return d;
}
void SpiffGenerator::getSpecials() {
// choose where the starting points and pools are
int x, y, p, t;
int edgeWaterA = (int)(_islandsFlagG * _totalMapSizeG / 16 + 0.5);
int edgeWaterB = (int)(_islandsFlagG * _totalMapSizeG / 16); // don't put pools between islands
// No matter what, they get a start hub spot.
if (_mirrorTypeG == MAXDISTMIRROR)
x = (int)((_totalMapSizeG * 3 + 8) / 16);
else
x = (int)(_mapMiddleMaxG / 2);
y = x;
_mapMiddle[x][y] = HUB; // hub start position
for (p = 1; p <= _numPoolsG; ++p) {
x = spiffRand(edgeWaterA, _mapMiddleMaxG - edgeWaterB);
y = spiffRand(edgeWaterA, _mapMiddleMaxG - edgeWaterB);
if (_mapMiddle[x][y] != UNASSIGNED)
--p; // repick this pool
else {
t = pickFrom3(SMALLPOOL, 40000 * _numPoolsG, MEDIUMPOOL, 20000 * _numPoolsG + _energyAmountG, LARGEPOOLTOP, 2 * _energyAmountG);
if (t == LARGEPOOLTOP) {
if ((y == _mapMiddleMaxG - edgeWaterB) || (_mapMiddle[x][y + 1] != UNASSIGNED))
t = SMALLPOOL; // keep large pool from being too high or overlapping another pool or start
else
_mapMiddle[x][y + 1] = LARGEPOOLBOTTOM;
}
_mapMiddle[x][y] = t;
}
}
}
void SpiffGenerator::copyMap(int XOffset, int YOffset, int XDirection, int YDirection) {
// copies the first quadrant of the map
// XOffset and YOffset are the distances moved
// XDirection and/or YDirection are/is -1 for mirrored, 1 for not mirrored
int x, y, tempMiddle, newCX;
int newCY = YOffset;
int newMX, newMY;
if (YDirection < 0)
newCY += _mapCornerMaxG;
for (y = 0; y <= _mapCornerMaxG; ++y) {
if (newCY < 0)
newCY += _totalMapSizeG;
else if (newCY >= _totalMapSizeG)
newCY -= _totalMapSizeG;
newCX = XOffset;
if (XDirection < 0)
newCX += _mapCornerMaxG;
for (x = 0; x <= _mapCornerMaxG; ++x) {
if (newCX < 0)
newCX += _totalMapSizeG;
else if (newCX >= _totalMapSizeG)
newCX -= _totalMapSizeG;
_mapCorner[newCX][newCY] = _mapCorner[x][y];
if ((x != _mapCornerMaxG) && (y != _mapCornerMaxG)) {
tempMiddle = _mapMiddle[x][y];
newMX = newCX;
newMY = newCY;
if (YDirection < 0) {
newMY--;
if (newMY == -1)
newMY = _totalMapSizeG - 1;
if (tempMiddle == LARGEPOOLTOP)
tempMiddle = LARGEPOOLBOTTOM;
else if (tempMiddle == LARGEPOOLBOTTOM)
tempMiddle = LARGEPOOLTOP;
}
if (XDirection < 0) {
newMX--;
if (newMX == -1)
newMX = _totalMapSizeG - 1;
}
_mapMiddle[newMX][newMY] = tempMiddle;
}
newCX += XDirection;
}
newCY += YDirection;
}
}
void SpiffGenerator::mirrorMap() {
// --------------------------------------------------------------
// mirror map
// --------------------------------------------------------------
int swapXa = pickFrom2(-1, 1, 1, _advancedMirrorOK_G);
int swapYa = pickFrom2(-1, _advancedMirrorOK_G, 1, 1);
int swapXb = pickFrom2(-1, _advancedMirrorOK_G, 1, 1);
int swapYb = pickFrom2(-1, 1, 1, _advancedMirrorOK_G);
switch (_mirrorTypeG) {
case NORMALMIRROR: // four quadrants
// ABCBA
// DEFED
// GHIHG
// DEFED
// ABCBA
copyMap(_mapCornerMaxG, 0, swapXa, swapYa);
copyMap(0, _mapCornerMaxG, swapXb, swapYb);
copyMap(_mapCornerMaxG, _mapCornerMaxG, swapXa * swapXb, swapYa * swapYb);
break;
case XOFFSETMIRROR: // Like normal, but one half is moved horizontally by 1/4 totalmapsize
// ABABABABA
// DEFGHGFED
// CDCDCDCDC
// FGHGFEDEF
// ABABABABA
if (swapYa == -1) // ensures fairness
swapXb = -1;
copyMap(_mapCornerMaxG, 0, 1, swapYa);
copyMap(_mapCornerMaxG / 2, _mapCornerMaxG, swapXb, swapYb);
copyMap(_mapCornerMaxG * 3 / 2, _mapCornerMaxG, swapXb, swapYa * swapYb);
break;
case YOFFSETMIRROR: // Like normal, but one half is moved vertically by 1/4 totalmapsize
if (swapXb == -1) // ensures fairness
swapYa = -1;
copyMap(_mapCornerMaxG, _mapCornerMaxG / 2, swapXa, swapYa);
copyMap(0, _mapCornerMaxG, swapXb, 1);
copyMap(_mapCornerMaxG, _mapCornerMaxG * 3 / 2, swapXa * swapXb, swapYa);
break;
case MAXDISTMIRROR: // Allows maximum distance between starting points
default:
// ABCDCBA
// E*GHIJE
// HIJE*GH
// DCBABCD
// HG*EJIH
// EJIHG*E
// ABCDCBA
copyMap(_mapCornerMaxG, 0, 1, -1);
copyMap(0, _mapCornerMaxG, -1, 1);
copyMap(_mapCornerMaxG, _mapCornerMaxG, -1, -1);
}
}
void SpiffGenerator::errorCorrection() {
// corrects errors caused by pool placement and mirroring
// doesn't correct _mapCorner[x][_totalMapSizeG+1] or _mapCorner[_totalMapSizeG+1][y], since it isn't used
// for any kElevHigh to kElevLow transitions, makes the kElevHigh kElevMedium
// for pools on nonflat terrain, makes the terrain kElevMedium
// removes invalid water
int x;
int y;
int tempX;
int tempY;
int dx;
int dy;
int redo;
int elev;
for (y = 0; y < _totalMapSizeG; ++y) {
for (x = 0; x < _totalMapSizeG; ++x) {
if (_mapCorner[x][y] == kElevHigh) {
for (dy = -1; dy <= 1; ++dy) {
tempY = y + dy;
if (tempY == _totalMapSizeG) {
tempY = 0;
} else if (tempY == -1) {
tempY = _totalMapSizeG - 1;
}
for (dx = -1; dx <= 1; ++dx) {
tempX = x + dx;
if (tempX == _totalMapSizeG) {
tempX = 0;
} else if (tempX == -1) {
tempX = _totalMapSizeG - 1;
}
if (_mapCorner[tempX][tempY] == kElevLow) {
_mapCorner[x][y] = kElevMedium;
}
}
}
} else if ((_mapCorner[x][y] != kElevLow) && (_mapCorner[x][y] != kElevMedium)) {
_mapCorner[x][y] = kElevMedium; // should not happen anymore
}
}
}
do {
redo = 0;
for (y = 0; y < _totalMapSizeG; ++y) {
for (x = 0; x < _totalMapSizeG; ++x) {
if (_mapMiddle[x][y] != UNASSIGNED) {
tempY = y + 1;
if (tempY == _totalMapSizeG)
tempY = 0;
tempX = x + 1;
if (tempX == _totalMapSizeG)
tempX = 0;
elev = _mapCorner[x][y];
if ((_mapMiddle[x][y] == WATER) && (elev != kElevLow))
_mapMiddle[x][y] = UNASSIGNED;
else if ((elev != _mapCorner[x][tempY]) || (elev != _mapCorner[tempX][y]) || (elev != _mapCorner[tempX][tempY])) {
if (_mapMiddle[x][y] == WATER)
_mapMiddle[x][y] = UNASSIGNED;
else {
_mapCorner[x][y] = kElevMedium;
_mapCorner[x][tempY] = kElevMedium;
_mapCorner[tempX][y] = kElevMedium;
_mapCorner[tempX][tempY] = kElevMedium;
redo = 1;
}
}
}
}
}
} while (redo);
}
void SpiffGenerator::generate() {
// --------------------------------------------------------------
// initialize
// --------------------------------------------------------------
int x;
int y;
int neighbors[MAXELEVVAL];
int a;
int b;
int tempElevation;
int nextElevation;
int special;
_mapCornerMaxG = _totalMapSizeG / 2;
_mapMiddleMaxG = _mapCornerMaxG - 1;
for (y = 0; y <= _mapCornerMaxG; ++y) {
// initialise map to UNASSIGNED tiles
for (x = 0; x <= _mapCornerMaxG; ++x) {
_mapCorner[x][y] = UNASSIGNED;
_mapMiddle[x][y] = UNASSIGNED;
}
}
if (_advancedMirrorOK_G)
_mirrorTypeG = pickFrom4(NORMALMIRROR, 1, XOFFSETMIRROR, 2, YOFFSETMIRROR, 2, MAXDISTMIRROR, 4);
else
_mirrorTypeG = NORMALMIRROR;
getSpecials(); // get start and pools
// --------------------------------------------------------------
// loop through each square
// --------------------------------------------------------------
_mapCorner[0][0] = pickFrom3(kElevLow, 1, kElevMedium, (_terrainSeedFlagG < 9), kElevHigh, (_terrainSeedFlagG < 8)); // seed
// fill in the rest of the random map
for (y = 0; y <= _mapCornerMaxG; ++y) {
for (x = 0; x <= _mapCornerMaxG; ++x) {
special = _mapMiddle[x][y]; // water wouldn't have been assigned yet, so must be pool, start, or UNASSIGNED
// --------------------------------------------------------------
// check neighbors
// --------------------------------------------------------------
if ((_mapCorner[x][y] != UNASSIGNED) && (_mapCorner[x][y] != LOW_OR_WATER))
nextElevation = _mapCorner[x][y]; // already defined because of a special or (0,0), so no change
else {
neighbors[kElevHigh] = 0;
neighbors[kElevMedium] = 0;
neighbors[kElevLow] = 0;
neighbors[WATER] = 0;
if (x > 0) {
a = _mapCorner[x - 1][y];
if ((y > 1) && (_mapMiddle[x - 1][y - 2] == WATER))
++neighbors[WATER];
if (y > 0)
neighbors[_mapCorner[x - 1][y - 1]] += 3;
} else {
a = _mapCorner[x][y - 1];
}
neighbors[a] += 3;
if (y > 0) {
b = _mapCorner[x][y - 1];
neighbors[b] += 3;
if (x < _mapCornerMaxG) {
++neighbors[_mapCorner[x + 1][y - 1]]; // so this value can be ignored when choosing water
if ((special != UNASSIGNED) && (x < _mapCornerMaxG - 1))
++neighbors[_mapCorner[x + 2][y - 1]];
}
if ((x > 1) && (_mapMiddle[x - 2][y - 1] == WATER))
++neighbors[WATER];
} else {
b = _mapCorner[x - 1][y]; // for probability equations for edges
}
// --------------------------------------------------------------
// pick new elevation
// --------------------------------------------------------------
// neighbors possible new elevation
// kElevHigh or kElevHigh with kElevMedium kElevHigh or kElevMedium
// kElevMedium only kElevHigh, kElevMedium or kElevLow
// kElevLow or WATER only kElevMedium, kElevLow or WATER
// kElevMedium with kElevLow or WATER kElevMedium or kElevLow, possible WATER if no kElevMedium left, down, or down-left
// kElevHigh with kElevLow or WATER kElevMedium
const int highAmt = 105;
const int mediumAmt = 100 + _waterAmountG;
const int lowAmt = 105 + 3 * _waterAmountG;
const int waterAmt = 15 * _waterAmountG;
if (neighbors[kElevLow]) {
if (neighbors[kElevHigh]) { // kElevHigh with kElevLow or WATER
nextElevation = kElevMedium;
} else if (neighbors[kElevMedium] >= 3) { // kElevMedium with kElevLow or WATER
if (a != b) {
nextElevation = pickFrom2(kElevLow, lowAmt, kElevMedium, mediumAmt);
} else if (a == kElevLow) {
nextElevation = pickFrom2(kElevLow, 100 * lowAmt, kElevMedium, mediumAmt * _cliffAmountG);
} else {
nextElevation = pickFrom2(kElevLow, lowAmt * _cliffAmountG, kElevMedium, 100 * mediumAmt);
}
} else { // kElevLow or WATER only, possibly kElevMedium down-right
if (neighbors[WATER] == 1) {
nextElevation = pickFrom3(WATER, 100 * waterAmt, kElevLow, 100 * lowAmt, kElevMedium, mediumAmt * _cliffAmountG);
} else if (neighbors[WATER] == 0) {
nextElevation = pickFrom3(WATER, waterAmt * _cliffAmountG, kElevLow, 100 * lowAmt, kElevMedium, mediumAmt * _cliffAmountG);
} else {
nextElevation = pickFrom3(WATER, 10000 * waterAmt, kElevLow, lowAmt * 100 * _cliffAmountG, kElevMedium, mediumAmt * _cliffAmountG * _cliffAmountG);
}
}
} else {
if (neighbors[kElevHigh]) { // kElevHigh or kElevHigh with kElevMedium
if (a != b) {
nextElevation = pickFrom2(kElevMedium, mediumAmt, kElevHigh, highAmt);
} else if (a == kElevHigh) {
nextElevation = pickFrom2(kElevMedium, mediumAmt * _cliffAmountG, kElevHigh, 100 * highAmt);
} else {
nextElevation = pickFrom2(kElevMedium, 100 * mediumAmt, kElevHigh, highAmt * _cliffAmountG);
}
} else {
nextElevation = pickFrom3(kElevLow, lowAmt * _cliffAmountG, kElevMedium, 200 * mediumAmt, kElevHigh, highAmt * _cliffAmountG);
}
}
// --------------------------------------------------------------
// set elevation
// --------------------------------------------------------------
if ((_mapCorner[x][y] == LOW_OR_WATER) && (nextElevation != WATER)) {
// bottom and left edges of a special on kElevLow ground there may only be kElevLow or WATER
nextElevation = kElevLow;
}
if (nextElevation == WATER) {
if ((x != 0) && (y != 0) && (_mapMiddle[x - 1][y - 1] == UNASSIGNED)) {
_mapMiddle[x - 1][y - 1] = WATER; // set WATER
}
nextElevation = kElevLow;
}
_mapCorner[x][y] = nextElevation; // set elevation
if (special != UNASSIGNED) { // if special, make flat spot (don't worry about going over map edge, will go into mirrored part)
tempElevation = nextElevation;
if (tempElevation == kElevLow)
tempElevation = LOW_OR_WATER; // allow for water on left and bottom edges
_mapCorner[x + 1][y + 1] = nextElevation;
_mapCorner[x + 1][y] = tempElevation;
_mapCorner[x][y + 1] = tempElevation;
}
}
}
}
if (_islandsFlagG) { // replace borders with water, errorCorrection() finishes it.
int edgeWaterA = (int)(_islandsFlagG * _totalMapSizeG / 16 + 0.5);
int edgeWaterB = _mapMiddleMaxG - (int)(_islandsFlagG * _totalMapSizeG / 16);
for (y = 0; y <= _mapCornerMaxG; ++y) {
for (x = 0; x < edgeWaterA; ++x) {
_mapCorner[x][y] = kElevLow;
_mapMiddle[x][y] = WATER;
}
if (_mapCorner[edgeWaterA + 1][y] == kElevHigh)
_mapCorner[edgeWaterA][y] = kElevMedium;
for (x = _mapMiddleMaxG; x > edgeWaterB; --x) {
_mapCorner[x + 1][y] = kElevLow;
_mapMiddle[x][y] = WATER;
}
if (_mapCorner[edgeWaterB][y] == kElevHigh) {
_mapCorner[edgeWaterB + 1][y] = kElevMedium;
}
}
for (x = edgeWaterA; x <= edgeWaterB + 1; ++x) {
for (y = 0; y < edgeWaterA; ++y) {
_mapCorner[x][y] = kElevLow;
_mapMiddle[x][y] = WATER;
}
if (_mapCorner[x][edgeWaterA + 1] == kElevHigh)
_mapCorner[x][edgeWaterA] = kElevMedium;
for (y = _mapMiddleMaxG; y > edgeWaterB; --y) {
_mapCorner[x][y + 1] = kElevLow;
_mapMiddle[x][y] = WATER;
}
if (_mapCorner[x][edgeWaterB] == kElevHigh) {
_mapCorner[x][edgeWaterB + 1] = kElevMedium;
}
}
if (_islandsFlagG == 2) { // add tiny islands to help bridge wide channels
int j;
for (int i = 0; i < _totalMapSizeG / 16; ++i) {
x = (int)(_totalMapSizeG / 16 - .5);
y = spiffRand(x, _totalMapSizeG / 2 - 1 - x);
if (spiffRand(0, 1)) {
x = _totalMapSizeG / 2 - 1 - x;
}
if (spiffRand(0, 1)) {
_mapMiddle[x][y] = UNASSIGNED;
for (j = 0; j < 4; ++j) {
_mapMiddle[x + spiffRand(-1, 1)][y + spiffRand(-1, 1)] = UNASSIGNED;
}
} else {
_mapMiddle[y][x] = UNASSIGNED;
for (j = 0; j < 4; ++j) {
_mapMiddle[y + spiffRand(-1, 1)][x + spiffRand(-1, 1)] = UNASSIGNED;
}
}
}
}
}
mirrorMap();
errorCorrection();
}
} // End of namespace Scumm

View File

@@ -0,0 +1,100 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_MAP_SPIFF_H
#define SCUMM_HE_MOONBASE_MAP_SPIFF_H
#ifdef ENABLE_HE
#include "engines/scumm/he/moonbase/map_mif.h"
#define MAXELEVVAL 4 // for array size
// These were "HIGH", "MEDIUM", and "LOW", but
// was renamed to prevent potential clashing
// with different platforms.
#define kElevHigh 3 // elevations
#define kElevMedium 2
#define kElevLow 1
#define WATER 0 // special types
#define HUB 1
#define SMALLPOOL 2
#define MEDIUMPOOL 3
#define LARGEPOOLTOP 4
#define LARGEPOOLBOTTOM 5 // placeholder for top half of a large pool
#define UNASSIGNED -1
#define LOW_OR_WATER -2
#define MAXSIZE 80
#define NORMALMIRROR 0
#define XOFFSETMIRROR 1
#define YOFFSETMIRROR 2
#define MAXDISTMIRROR 3
namespace Scumm {
class SpiffGenerator {
public:
SpiffGenerator(int seed);
~SpiffGenerator() = default;
MapFile *generateMap(int water, int tileset, int mapSize, int energy, int terrain);
private:
int _seed = 0;
int _numPoolsG = 0; // per quadrant
int _energyAmountG = 0; // 2048 = min energy on small map, 51200 = max energy on max map, etc.
int _cliffAmountG = 0; // amount of cliffs, 10 is min, 70 is max
int _waterAmountG = 0; // 0 is min, 30 is max
int _totalMapSizeG = 0;
int _terrainSeedFlagG = 0; // disables kElevHigh or kElevLow terrain for the initial elevation when appropriate
int _islandsFlagG = 0; // enables islands
int _advancedMirrorOK_G = 0; // low terrain roughness can leave too abrupt changes at the edge, so set false to disable some mirroring types
int _mirrorTypeG = 0; // what mirroring is used
int _mapCornerMaxG = 0; // size of random section
int _mapMiddleMaxG = 0;
int _mapCorner[MAXSIZE+1][MAXSIZE+1] = { {}, {} };
int _mapMiddle[MAXSIZE][MAXSIZE] = { {}, {} };
float getRandomFloat();
int spiffRand(int min, int max);
int pickFrom2(int a, int probA, int b, int probB);
int pickFrom3(int a, int probA, int b, int probB, int c, int probC);
int pickFrom4(int a, int probA, int b, int probB, int c, int probC, int d, int probD);
void getSpecials();
void copyMap(int XOffset, int YOffset, int XDirection, int YDirection);
void mirrorMap();
void errorCorrection();
void generate();
};
} // End of namespace Scumm
#endif // ENABLE_HE
#endif // SCUMM_HE_MOONBASE_MAP_SPIFF_H

View File

@@ -0,0 +1,85 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/formats/winexe_pe.h"
#include "scumm/he/intern_he.h"
#include "scumm/he/moonbase/moonbase.h"
#include "scumm/he/moonbase/ai_main.h"
#include "scumm/he/moonbase/map_main.h"
namespace Scumm {
Moonbase::Moonbase(ScummEngine_v100he *vm) : _vm(vm) {
_exe = new Common::PEResources();
initFOW();
_ai = new AI(_vm);
_map = new Map(_vm);
}
Moonbase::~Moonbase() {
delete _exe;
delete _ai;
delete _map;
}
int Moonbase::readFromArray(int array, int y, int x) {
_vm->VAR(_vm->VAR_U32_RESERVED) = array;
return _vm->readArray(_vm->VAR_U32_RESERVED, y, x);
}
void Moonbase::deallocateArray(int array) {
_vm->VAR(_vm->VAR_U32_RESERVED) = array;
return _vm->nukeArray(_vm->VAR_U32_RESERVED);
}
int Moonbase::callScummFunction(int scriptNumber, int paramCount,...) {
va_list va_params;
va_start(va_params, paramCount);
int args[25];
memset(args, 0, sizeof(args));
Common::String str;
str = Common::String::format("Moonbase::callScummFunction(%d, [", scriptNumber);
for (int i = 0; i < paramCount; i++) {
args[i] = va_arg(va_params, int);
str += Common::String::format("%d ", args[i]);
}
str += "])";
debug(3, "%s", str.c_str());
va_end(va_params);
_vm->runScript(scriptNumber, 0, 1, args);
return _vm->pop();
}
} // End of namespace Scumm

View File

@@ -0,0 +1,111 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCUMM_HE_MOONBASE_MOONBASE_H
#define SCUMM_HE_MOONBASE_MOONBASE_H
#ifdef ENABLE_HE
namespace Common {
class PEResources;
}
namespace Scumm {
class AI;
class Map;
class Moonbase {
public:
Moonbase(ScummEngine_v100he *vm);
~Moonbase();
int readFromArray(int array, int y, int x);
void deallocateArray(int array);
int callScummFunction(int scriptNumber, int paramCount,...);
// FOW Stuff
bool isFOW(int resNum, int state, uint32 conditionBits) {
return resNum == _fowSentinelImage && state == _fowSentinelState && conditionBits == _fowSentinelConditionBits;
}
void initFOW();
void releaseFOWResources();
bool setFOWImage(int id);
void setFOWInfo(int fowInfoArray, int downDim, int acrossDim, int viewX, int viewY, int clipX1,
int clipY1, int clipX2, int clipY2, int technique, int nFrame);
void renderFOW(WizMultiTypeBitmap *destSurface);
private:
int readFOWVisibilityArray(int array, int y, int x);
void renderFOWState(WizMultiTypeBitmap *destSurface, int x, int y, int state);
public:
int _fowSentinelImage;
int _fowSentinelState;
uint32 _fowSentinelConditionBits;
AI *_ai;
Map *_map;
private:
ScummEngine_v100he *_vm;
int _fowFrameBaseNumber;
int _fowAnimationFrames;
int _fowCurrentFOWFrame;
int32 _fowTileW;
int32 _fowTileH;
uint8 *_fowImage;
int _fowClipX1;
int _fowClipY1;
int _fowClipX2;
int _fowClipY2;
int _fowDrawX;
int _fowDrawY;
int _fowVtx1;
int _fowVty1;
int _fowMvx;
int _fowMvy;
int _fowVw;
int _fowVh;
bool _fowBlackMode;
int32 _fowRenderTable[32768];
Common::PEResources *_exe;
Common::Path _fileName;
};
} // End of namespace Scumm
#endif // ENABLE_HE
#endif // SCUMM_HE_MOONBASE_H

View File

@@ -0,0 +1,440 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "common/formats/winexe_pe.h"
#include "scumm/he/intern_he.h"
#include "scumm/he/moonbase/moonbase.h"
namespace Scumm {
#define FOW_ANIM_FRAME_COUNT 38
void Moonbase::initFOW() {
_fowSentinelImage = -1;
_fowSentinelState = -1;
_fowSentinelConditionBits = 0;
_fowFrameBaseNumber = 0;
_fowAnimationFrames = 1;
_fowCurrentFOWFrame = 0;
_fowTileW = 0;
_fowTileH = 0;
_fowImage = nullptr;
_fowClipX1 = 0;
_fowClipY1 = 0;
_fowClipX2 = 0;
_fowClipY2 = 0;
_fowDrawX = 0;
_fowDrawY = 0;
_fowVtx1 = 0;
_fowVty1 = 0;
_fowMvx = 0;
_fowMvy = 0;
_fowVw = 0;
_fowVh = 0;
_fowBlackMode = true;
memset(_fowRenderTable, 0, sizeof(_fowRenderTable));
}
void Moonbase::releaseFOWResources() {
if (_fowImage) {
free(_fowImage);
_fowImage = nullptr;
}
}
bool Moonbase::setFOWImage(int image) {
releaseFOWResources();
if (!_fowImage) {
Common::String fowImageFilename(ConfMan.get("MOONX_FOWImageFilename").c_str());
#if 0 // TODO
if (!fowImageFilename.empty()) {
void *wiz = loadWizFromFilename(fowImageFilename);
if (wiz) {
captureFOWImageFromLocation(wiz, file.size());
free(wiz);
}
}
#endif
if (!_fowImage && image < 0) {
int resId;
// PIECES BUBBLES CIRCLES SIMPLE* WEDGEY BUBBLE2
// WEDGE2 SPIKEY ANGLES SMOOTHED WUZZY SYS7-BEVELED
if (image >= -12 && image <= -1)
resId = 210 - image; // 211-222 range
else
resId = 214; // default, SIMPLE
if (_fileName.empty()) { // We are running for the first time
_fileName = _vm->generateFilename(-3);
if (!_exe->loadFromEXE(_fileName))
error("Cannot open file %s", _fileName.toString(Common::Path::kNativeSeparator).c_str());
}
Common::SeekableReadStream *stream = _exe->getResource(Common::kWinRCData, resId);
if (stream->size()) {
_fowImage = (uint8 *)malloc(stream->size());
stream->read(_fowImage, stream->size());
}
delete stream;
}
if (!_fowImage && image > 0) {
int sz = _vm->getResourceSize(rtImage, image);
_fowImage = (uint8 *)malloc(sz);
// We have to copy it, otherwise the resource manager
// will kill it earlier or later. Matches original.
memcpy(_fowImage, _vm->getResourceAddress(rtImage, image), sz);
}
if (!_fowImage)
return false;
}
int nStates = _vm->_wiz->getWizImageStates(_fowImage);
if (nStates > FOW_ANIM_FRAME_COUNT) {
releaseFOWResources();
return false;
}
_fowAnimationFrames = (nStates + FOW_ANIM_FRAME_COUNT - 1) / FOW_ANIM_FRAME_COUNT;
_vm->_wiz->getWizImageDim(_fowImage, (nStates - 1), _fowTileW, _fowTileH);
bool sizeIndicatorWasBlack = true;
int32 hitTestValue = 0;
int32 pixelValue = 0;
_vm->_wiz->moonbaseLayeredWizHitTest(
&hitTestValue, &pixelValue, _fowImage, (nStates - 1), 0, 0, 0, 0);
sizeIndicatorWasBlack = (pixelValue == 0);
if (ConfMan.hasKey("EnableFOWRects"))
_fowBlackMode = (ConfMan.getInt("EnableFOWRects") == 1);
else
_fowBlackMode = sizeIndicatorWasBlack;
return true;
}
enum FOWElement {
FOW_EMPTY = 0,
FOW_SOLID = 1,
FF_L = 0x01,
FF_R = 0x02,
FF_T = 0x04,
FF_B = 0x08,
FF_T_L = 0x10,
FF_T_R = 0x20,
FF_B_L = 0x40,
FF_B_R = 0x80,
FF_Q_A = (FF_L | FF_T | FF_T_L),
FF_Q_B = (FF_R | FF_T | FF_T_R),
FF_Q_C = (FF_L | FF_B | FF_B_L),
FF_Q_D = (FF_R | FF_B | FF_B_R)
};
int Moonbase::readFOWVisibilityArray(int array, int y, int x) {
if (readFromArray(array, x, y) > 0)
return FOW_EMPTY;
return FOW_SOLID;
}
void Moonbase::setFOWInfo(int fowInfoArray, int downDim, int acrossDim, int viewX, int viewY, int clipX1,
int clipY1, int clipX2, int clipY2, int technique, int nFrame) {
if (!_fowImage)
return;
memset(_fowRenderTable, 0, sizeof(_fowRenderTable));
_fowDrawX = clipX1;
_fowDrawY = clipY1;
_fowClipX1 = clipX1;
_fowClipY1 = clipY1;
_fowClipX2 = clipX2;
_fowClipY2 = clipY2;
// Figure out the number of tiles are involved
int view_W = (clipX2 - clipX1) + 1;
int view_H = (clipY2 - clipY1) + 1;
int tw = _fowTileW;
int th = _fowTileH;
int dw = acrossDim;
int dh = downDim;
int dlw = dw * tw;
int dlh = dh * th;
_fowMvx = (0 <= viewX) ? (viewX % dlw) : (dlw - (-viewX % dlw));
_fowMvy = (0 <= viewY) ? (viewY % dlh) : (dlh - (-viewY % dlh));
_fowVtx1 = _fowMvx / tw;
_fowVty1 = _fowMvy / th;
_fowVw = (((_fowMvx + view_W + tw - 1) / tw) - _fowVtx1) + 1;
_fowVh = (((_fowMvy + view_H + th - 1) / th) - _fowVty1) + 1;
// Build the connectivity table
int t = (_fowVty1 - 1); if (t >= dh) { t = 0; } else if (t < 0) { t = (dh - 1); }
int m = (_fowVty1 + 0); if (m >= dh) { m = 0; } else if (m < 0) { m = (dh - 1); }
int b = (_fowVty1 + 1); if (b >= dh) { b = 0; } else if (b < 0) { b = (dh - 1); }
int il = (_fowVtx1 - 1); if (il >= dh) { il = 0; } else if (il < 0) { il = (dw - 1); }
int ic = (_fowVtx1 + 0); if (ic >= dh) { ic = 0; } else if (ic < 0) { ic = (dw - 1); }
int ir = (_fowVtx1 + 1); if (ir >= dh) { ir = 0; } else if (ir < 0) { ir = (dw - 1); }
int dataOffset = (_fowVw * 3);
int dataOffset2 = (dataOffset * 2);
int32 *pOutterRenderTableA = _fowRenderTable;
int32 *pOutterRenderTableB = pOutterRenderTableA + dataOffset;
for (int ay = 0; ay < _fowVh; ay++) {
int l = il;
int c = ic;
int r = ir;
int32 *pRenderTableA = pOutterRenderTableA;
int32 *pRenderTableB = pOutterRenderTableB;
pOutterRenderTableA += dataOffset2;
pOutterRenderTableB += dataOffset2;
for (int ax = 0; ax < _fowVw; ax++) {
int visibility = readFOWVisibilityArray(fowInfoArray, m, c);
if (visibility == FOW_EMPTY) {
uint32 bits = 0;
if (readFOWVisibilityArray(fowInfoArray, t, l) != 0) bits |= FF_T_L;
if (readFOWVisibilityArray(fowInfoArray, t, c) != 0) bits |= FF_T;
if (readFOWVisibilityArray(fowInfoArray, t, r) != 0) bits |= FF_T_R;
if (readFOWVisibilityArray(fowInfoArray, m, l) != 0) bits |= FF_L;
if (readFOWVisibilityArray(fowInfoArray, m, r) != 0) bits |= FF_R;
if (readFOWVisibilityArray(fowInfoArray, b, l) != 0) bits |= FF_B_L;
if (readFOWVisibilityArray(fowInfoArray, b, c) != 0) bits |= FF_B;
if (readFOWVisibilityArray(fowInfoArray, b, r) != 0) bits |= FF_B_R;
if (bits) {
*pRenderTableA++ = 1;
*pRenderTableB++ = 1;
// Quadrant (A)
if (bits & FF_Q_A) {
*pRenderTableA++ = (
((FF_L & bits) ? 1 : 0) |
((FF_T & bits) ? 2 : 0) |
((FF_T_L & bits) ? 4 : 0)
) + 0;
} else {
*pRenderTableA++ = 0;
}
// Quadrant (B)
if (bits & FF_Q_B) {
*pRenderTableA++ = (
((FF_R & bits) ? 1 : 0) |
((FF_T & bits) ? 2 : 0) |
((FF_T_R & bits) ? 4 : 0)
) + 8;
} else {
*pRenderTableA++ = 0;
}
// Quadrant (C)
if (bits & FF_Q_C) {
*pRenderTableB++ = (
((FF_L & bits) ? 1 : 0) |
((FF_B & bits) ? 2 : 0) |
((FF_B_L & bits) ? 4 : 0)
) + 16;
} else {
*pRenderTableB++ = 0;
}
// Quadrant (D)
if (bits & FF_Q_D) {
*pRenderTableB++ = (
((FF_R & bits) ? 1 : 0) |
((FF_B & bits) ? 2 : 0) |
((FF_B_R & bits) ? 4 : 0)
) + 24;
} else {
*pRenderTableB++ = 0;
}
} else {
*pRenderTableA++ = 0;
*pRenderTableB++ = 0;
}
} else {
if (_fowBlackMode) {
*pRenderTableA++ = 2;
*pRenderTableB++ = 2;
} else {
*pRenderTableA++ = 1;
*pRenderTableA++ = 33;
*pRenderTableA++ = 34;
*pRenderTableB++ = 1;
*pRenderTableB++ = 35;
*pRenderTableB++ = 36;
}
}
if (++l >= dw) { l = 0; }
if (++c >= dw) { c = 0; }
if (++r >= dw) { r = 0; }
}
if (++t >= dh) { t = 0; }
if (++m >= dh) { m = 0; }
if (++b >= dh) { b = 0; }
}
_fowCurrentFOWFrame = (nFrame >= 0) ? (nFrame % _fowAnimationFrames) : ((-nFrame) % _fowAnimationFrames);
_fowFrameBaseNumber = (_fowCurrentFOWFrame * FOW_ANIM_FRAME_COUNT);
}
void Moonbase::renderFOWState(WizMultiTypeBitmap *destSurface,int x, int y, int state) {
int32 spotx, spoty;
_vm->_wiz->getWizImageSpot(_fowImage, state, spotx, spoty);
_vm->_wiz->drawMoonbaseLayeredWiz(
destSurface->data,
destSurface->width, destSurface->height,
destSurface->stride, destSurface->format, destSurface->bpp,
_fowImage, x - spotx, y - spoty, state,
_fowClipX1, _fowClipY1, _fowClipX2, _fowClipY2,
0, 0, nullptr, nullptr);
}
static void blackRect_16bpp(WizMultiTypeBitmap *destSurface, int x1, int y1, int x2, int y2) {
byte *dst = destSurface->data + (destSurface->stride * y1) + (x1 * sizeof(WizRawPixel16));
int h = (y2 - y1) + 1;
int writeBytes = ((x2 - x1) + 1) * sizeof(WizRawPixel16);
while (--h >= 0) {
memset(dst, 0, writeBytes);
dst += destSurface->stride;
}
}
void Moonbase::renderFOW(WizMultiTypeBitmap *destSurface) {
if (!_fowImage)
return;
const int32 *outerRenderTable = _fowRenderTable;
int ixPos = ((_fowVtx1 * _fowTileW) - _fowMvx) + _fowDrawX;
int yPos = ((_fowVty1 * _fowTileH) - _fowMvy) + _fowDrawY;
int dataOffset = _fowVw * 3;
int halfTileHeight = _fowTileH / 2;
int cx2 = MIN<int>(_fowClipX2, (int)(destSurface->width - 1));
int cy2 = MIN<int>(_fowClipY2, (int)(destSurface->height - 1));
for (int ry = 0; ry < _fowVh; ry++) {
int realYPos = yPos;
for (int i = 0; i < 2; i++) {
const int32 *renderTable = outerRenderTable;
outerRenderTable += dataOffset;
int xPos = ixPos;
for (int rx = 0; rx < _fowVw; rx++) {
int state = *renderTable++;
if (state != 0) {
if (state == 2) {
int countLeft = (_fowVw - rx);
int count = 0;
for (; count < countLeft; count++) {
if (*(renderTable + count) != 2)
break;
renderTable++;
rx++;
}
count++;
int x1 = xPos;
int y1 = realYPos;
xPos += _fowTileW * count;
int x2 = (xPos - 1);
int y2 = ((y1 + halfTileHeight) - 1);
x1 = MAX(0, x1);
y1 = MAX(0, y1);
x2 = MIN(x2, cx2);
y2 = MIN(y2, cy2);
if ((x2 >= x1) && (y2 >= y1) && (x1 <= _fowClipX2) && (y1 <= _fowClipY2))
blackRect_16bpp(destSurface, x1, y1, x2, y2);
} else {
int subState;
if ((subState = *renderTable++) != 0)
renderFOWState(destSurface, xPos, yPos, (subState + _fowFrameBaseNumber));
if ((subState = *renderTable++) != 0)
renderFOWState(destSurface, xPos, yPos, (subState + _fowFrameBaseNumber));
xPos += _fowTileW;
}
} else {
xPos += _fowTileW;
}
}
realYPos += halfTileHeight;
}
yPos += _fowTileH;
}
}
} // End of namespace Scumm

File diff suppressed because it is too large Load Diff