1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-14 00:54:05 +02:00

#11 controls mapping; saving settings

This commit is contained in:
XProger
2018-02-25 07:03:28 +03:00
parent 6acb509ab4
commit 3009f2387a
14 changed files with 324 additions and 136 deletions

View File

@@ -448,7 +448,7 @@ struct Camera : ICamera {
virtual void update() {
if (shake > 0.0f) {
shake = max(0.0f, shake - Core::deltaTime);
osJoyVibrate(Core::settings.controls[cameraIndex].joyIndex, clamp(shake, 0.0f, 1.0f), 0);
Input::setJoyVibrate(cameraIndex, clamp(shake, 0.0f, 1.0f), 0);
}
if (mode == MODE_CUTSCENE) {

View File

@@ -164,12 +164,6 @@
#define GENERATE_WATER_PLANE
#endif
extern int osGetTime();
extern bool osSave(const char *name, const void *data, int size);
extern bool osJoyReady(int index);
extern void osJoyVibrate(int index, float L, float R);
#include "utils.h"
extern void* osMutexInit ();
@@ -177,6 +171,18 @@ extern void osMutexFree (void *obj);
extern void osMutexLock (void *obj);
extern void osMutexUnlock (void *obj);
extern int osGetTime ();
extern bool osJoyReady (int index);
extern void osJoyVibrate (int index, float L, float R);
extern bool osCacheWrite (const char *name, const char *data, int size);
extern Stream* osCacheRead (const char *name);
extern bool osSaveGame (const char *data, int size);
extern Stream* osLoadGame ();
struct Mutex {
void *obj;
@@ -254,8 +260,8 @@ enum ControlKey {
};
struct KeySet {
InputKey key;
JoyKey joy;
uint8 key;
uint8 joy;
KeySet() {}
KeySet(InputKey key, JoyKey joy) : key(key), joy(joy) {}
@@ -285,10 +291,14 @@ namespace Core {
#endif
} support;
#define SETTINGS_VERSION 1
struct Settings {
enum Quality { LOW, MEDIUM, HIGH };
enum Stereo { STEREO_OFF, STEREO_ON, STEREO_SPLIT };
uint8 version;
struct {
union {
struct {
@@ -342,7 +352,9 @@ namespace Core {
KeySet keys[cMAX];
} controls[2];
uint8 playerIndex; // temporary, used only for setting controls
// temporary, used only for setting controls
uint8 playerIndex;
uint8 ctrlIndex;
} settings;
bool resetState;
@@ -1021,6 +1033,8 @@ namespace Core {
whiteTex = new Texture(1, 1, Texture::RGBA, Texture::NEAREST, &data);
// init settings
settings.version = SETTINGS_VERSION;
settings.detail.setFilter (Core::Settings::HIGH);
settings.detail.setLighting (Core::Settings::HIGH);
settings.detail.setShadows (Core::Settings::HIGH);
@@ -1076,7 +1090,7 @@ namespace Core {
ctrl.keys[ cDash ] = KeySet( ikNone, jkRT );
ctrl.keys[ cRoll ] = KeySet( ikNone, jkB );
ctrl.keys[ cInventory ] = KeySet( ikNone, jkSelect );
ctrl.keys[ cStart ] = KeySet( ikEnter, jkStart );
ctrl.keys[ cStart ] = KeySet( ikNone, jkStart );
}
// use D key for jump in browsers

View File

@@ -34,10 +34,22 @@ namespace Game {
if (level) level->stopChannel(channel);
}
void readSettings() {
Stream *stream = osCacheRead("settings");
if (!stream) return;
uint8 version;
stream->read(version);
if (version == SETTINGS_VERSION && stream->size == sizeof(Core::Settings))
stream->raw((char*)&Core::settings + 1, stream->size - 1); // read settings data right after version number
delete stream;
}
void init(Stream *lvl) {
nextLevel = NULL;
Core::init();
readSettings();
shaderCache = new ShaderCache();
UI::init(level);
@@ -104,7 +116,7 @@ namespace Game {
return true;
Input::update();
/*
if (level->camera) {
if (Input::down[ikV]) { // third <-> first person view
level->camera->changeView(!level->camera->firstPerson);
@@ -122,7 +134,7 @@ namespace Game {
level->loadGame(0);
Input::down[ikL] = false;
}
*/
if (!level->level.isTitle()) {
if (Input::state[0][cStart]) level->addPlayer(0);
if (Input::state[1][cStart]) level->addPlayer(1);

View File

@@ -38,8 +38,8 @@ namespace TR {
VER_TR3_PC = VER_TR3 | VER_PC,
VER_TR3_PSX = VER_TR3 | VER_PSX,
VER_MAX = 0xFFFFFFFF,
VER_MAX = 0xFFFFFFFF,
};
enum LevelID {
@@ -506,6 +506,9 @@ namespace TR {
if (Stream::existsContent("data/JUNGLE.TR2"))
return VER_TR3_PC;
if (Stream::existsContent("DATA/JUNGLE.PSX"))
return VER_TR3_PSX;
useEasyStart = false;
return VER_UNKNOWN;
}
@@ -542,6 +545,7 @@ namespace TR {
}
case VER_TR2_PSX : sprintf(dst, "DATA/%s.PSX", LEVEL_INFO[id].name); break;
case VER_TR3_PC : sprintf(dst, isCutsceneLevel(id) ? "cuts/%s.TR2" : "data/%s.TR2", LEVEL_INFO[id].name); break;
case VER_TR3_PSX : sprintf(dst, isCutsceneLevel(id) ? "CUTS/%s.PSX" : "DATA/%s.PSX", LEVEL_INFO[id].name); break;
default : ASSERT(false);
}
} else {

View File

@@ -7,7 +7,7 @@
#define INPUT_JOY_COUNT 4
namespace Input {
InputKey lastKey;
bool down[ikMAX];
bool state[2][cMAX];
@@ -19,9 +19,10 @@ namespace Input {
} mouse;
struct Joystick {
vec2 L, R;
float LT, RT;
bool down[jkMAX];
vec2 L, R;
float LT, RT;
JoyKey lastKey;
bool down[jkMAX];
} joy[INPUT_JOY_COUNT];
struct Touch {
@@ -77,6 +78,8 @@ namespace Input {
default : ;
}
down[key] = value;
if (value && key <= ikZ) lastKey = key;
}
void setPos(InputKey key, const vec2 &pos) {
@@ -95,7 +98,12 @@ namespace Input {
}
void setJoyDown(int index, JoyKey key, bool value) {
if (joy[index].down[key] == value)
return;
joy[index].down[key] = value;
if (value) joy[index].lastKey = key;
}
void setJoyPos(int index, JoyKey key, const vec2 &pos) {
@@ -109,6 +117,12 @@ namespace Input {
setJoyDown(index, key, pos.x > 0.0f); // gamepad LT, RT auto-down state
}
void setJoyVibrate(int playerIndex, float L, float R) {
if (!Core::settings.controls[playerIndex].vibration)
return;
osJoyVibrate(Core::settings.controls[playerIndex].joyIndex, L, R);
}
InputKey getTouch(int id) {
for (int i = 0; i < COUNT(touch); i++)
if (down[ikTouchA + i] && touch[i].id == id)

View File

@@ -17,12 +17,15 @@
#define TITLE_LOADING 64.0f
#define LINE_HEIGHT 20.0f
static const struct OptionItem *waitForKey = NULL;
struct OptionItem {
enum Type {
TYPE_TITLE,
TYPE_EMPTY,
TYPE_BUTTON,
TYPE_PARAM,
TYPE_KEY,
} type;
StringID title;
intptr_t offset;
@@ -31,7 +34,11 @@ struct OptionItem {
uint8 maxValue;
bool bar;
OptionItem(Type type = TYPE_EMPTY, StringID title = STR_NOT_IMPLEMENTED, intptr_t offset = 0, uint32 color = 0xFFFFFFFF, int icon = 0, uint8 maxValue = 0, bool bar = false) : type(type), title(title), offset(offset), color(color), icon(icon), maxValue(maxValue), bar(bar) {}
OptionItem(Type type = TYPE_EMPTY, int title = STR_NOT_IMPLEMENTED, intptr_t offset = 0, uint32 color = 0xFFFFFFFF, int icon = 0, uint8 maxValue = 0, bool bar = false) : type(type), title(StringID(title)), offset(offset), color(color), icon(icon), maxValue(maxValue), bar(bar) {}
void setValue(uint8 value, Core::Settings *settings) const {
*(uint8*)(intptr_t(settings) + offset) = value;
}
float drawParam(float x, float y, float w, StringID oStr, bool active, uint8 value) const {
if (oStr != STR_NOT_IMPLEMENTED) {
@@ -40,12 +47,22 @@ struct OptionItem {
w = w * 0.5f - 32.0f;
}
UI::textOut(vec2(x, y), StringID(color + int(value)), UI::aCenter, w); // color as StringID
if (active) {
StringID vStr = StringID(color + int(value));
uint8 alpha = 255;
if (type == TYPE_KEY && waitForKey == this) {
vStr = STR_PRESS_ANY_KEY;
float t = (Core::getTime() % 1000) / 1000.0f;
t = 0.2f + (sinf(t * PI * 2) * 0.5f + 0.5f) * 0.8f;
alpha = uint8(t * 255.0f);
}
UI::textOut(vec2(x, y), vStr, UI::aCenter, w, alpha, UI::SHADE_GRAY); // color as StringID
if (type == TYPE_PARAM && active) {
float maxWidth = UI::getTextSize(STR[color + value]).x;
maxWidth = maxWidth * 0.5f + 8.0f;
x += w * 0.5f;
if (value > 0) UI::specOut(vec2(x - maxWidth - 16.0f, y), 108);
if (value < maxValue) UI::specOut(vec2(x + maxWidth, y), 109);
}
@@ -71,7 +88,7 @@ struct OptionItem {
switch (type) {
case TYPE_TITLE :
UI::renderBar(UI::BAR_OPTION, vec2(x, y - LINE_HEIGHT + 6), vec2(w, LINE_HEIGHT - 6), 1.0f, 0x802288FF, 0, 0, 0);
UI::textOut(vec2(x, y), title, UI::aCenter, w, UI::SHADE_GRAY);
UI::textOut(vec2(x, y), title, UI::aCenter, w, 255, UI::SHADE_GRAY);
case TYPE_EMPTY : break;
case TYPE_BUTTON : {
const char *caption = offset ? (char*)offset : STR[title];
@@ -79,6 +96,7 @@ struct OptionItem {
break;
}
case TYPE_PARAM :
case TYPE_KEY :
return bar ? drawBar(x, y, w, active, value) : drawParam(x, y, w, title, active, value);
}
@@ -116,11 +134,27 @@ static const OptionItem optSound[] = {
static const OptionItem optControls[] = {
OptionItem( OptionItem::TYPE_TITLE, STR_SET_CONTROLS ),
OptionItem( ),
OptionItem( OptionItem::TYPE_PARAM, STR_NOT_IMPLEMENTED, SETTINGS( playerIndex ), STR_PLAYER_1, 0, 1 ),
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_CONTROLS_GAMEPAD, SETTINGS( controls[0].joyIndex ), STR_GAMEPAD_1, 0, 3 ),
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_CONTROLS_VIBRATION, SETTINGS( controls[0].vibration ), STR_OFF, 0, 1 ),
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_CONTROLS_RETARGET, SETTINGS( controls[0].retarget ), STR_OFF, 0, 1 ),
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_CONTROLS_MULTIAIM, SETTINGS( controls[0].multiaim ), STR_OFF, 0, 1 ),
OptionItem( OptionItem::TYPE_PARAM, STR_NOT_IMPLEMENTED , SETTINGS( playerIndex ), STR_PLAYER_1, 0, 1 ),
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_CONTROLS_GAMEPAD , SETTINGS( controls[0].joyIndex ), STR_GAMEPAD_1, 0, 3 ),
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_CONTROLS_VIBRATION , SETTINGS( controls[0].vibration ), STR_OFF, 0, 1 ),
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_CONTROLS_RETARGET , SETTINGS( controls[0].retarget ), STR_OFF, 0, 1 ),
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_CONTROLS_MULTIAIM , SETTINGS( controls[0].multiaim ), STR_OFF, 0, 1 ),
OptionItem( OptionItem::TYPE_PARAM, STR_NOT_IMPLEMENTED , SETTINGS( ctrlIndex ), STR_OPT_CONTROLS_KEYBOARD, 0, 1 ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cUp , SETTINGS( controls[0].keys[ cUp ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cDown , SETTINGS( controls[0].keys[ cDown ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cRight , SETTINGS( controls[0].keys[ cRight ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cLeft , SETTINGS( controls[0].keys[ cLeft ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cWalk , SETTINGS( controls[0].keys[ cWalk ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cJump , SETTINGS( controls[0].keys[ cJump ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cAction , SETTINGS( controls[0].keys[ cAction ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cWeapon , SETTINGS( controls[0].keys[ cWeapon ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cLook , SETTINGS( controls[0].keys[ cLook ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cDuck , SETTINGS( controls[0].keys[ cDuck ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cDash , SETTINGS( controls[0].keys[ cDash ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cRoll , SETTINGS( controls[0].keys[ cRoll ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cInventory , SETTINGS( controls[0].keys[ cInventory ] ), STR_KEY_FIRST ),
OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cStart , SETTINGS( controls[0].keys[ cStart ] ), STR_KEY_FIRST ),
};
static OptionItem optControlsPlayer[COUNT(optControls)];
@@ -269,9 +303,19 @@ struct Inventory {
case TR::Entity::INV_CONTROLS :
ASSERT(optControls[2].offset == SETTINGS( playerIndex) );
for (int i = 0; i < COUNT(optControls); i++) {
optControlsPlayer[i] = optControls[i];
if (i > 2)
optControlsPlayer[i].offset += sizeof(Core::Settings::Controls) * Core::settings.playerIndex;
OptionItem &opt = optControlsPlayer[i];
opt = optControls[i];
if (i > 2 && i != 2 && i != 7)
opt.offset += sizeof(Core::Settings::Controls) * Core::settings.playerIndex;
if (i > 7) {
if (Core::settings.ctrlIndex == 1) {
opt.offset++; // add offset to joy
opt.color = STR_JOY_FIRST;
} else
opt.color = STR_KEY_FIRST;
}
}
optCount = COUNT(optControlsPlayer);
return optControlsPlayer;
@@ -305,7 +349,7 @@ struct Inventory {
uint8 &value = *(uint8*)(intptr_t(settings) + opt->offset);
switch (key) {
case cAction : return (opt->type == OptionItem::TYPE_BUTTON) ? opt : NULL;
case cAction : return (opt->type == OptionItem::TYPE_BUTTON || opt->type == OptionItem::TYPE_KEY) ? opt : NULL;
case cUp : nextSlot(slot, -1); break;
case cDown : nextSlot(slot, +1); break;
case cLeft :
@@ -455,6 +499,8 @@ struct Inventory {
phaseRing = phasePage = phaseChoose = phaseSelect = 0.0f;
memset(pageItemIndex, 0, sizeof(pageItemIndex));
waitForKey = NULL;
}
~Inventory() {
@@ -584,6 +630,9 @@ struct Inventory {
}
bool toggle(int playerIndex = 0, Page curPage = PAGE_INVENTORY, TR::Entity::Type type = TR::Entity::LARA) {
if (!game->getLara(playerIndex))
return false;
this->playerIndex = playerIndex;
titleTimer = 0.0f;
@@ -714,6 +763,7 @@ struct Inventory {
}
case TR::Entity::INV_CONTROLS :
Core::settings.playerIndex = 0;
Core::settings.ctrlIndex = 0;
break;
case TR::Entity::INV_DETAIL :
settings = Core::settings;
@@ -769,13 +819,6 @@ struct Inventory {
nextLevel = level->getHomeId();
toggle();
}
if ((key == cInventory || key == cJump) && phaseChoose == 1.0f) {
chosen = false;
item->anim->dir = 1.0f;
item->value = 1000;
item->angle = 0.0f;
}
}
void optionChanged(Item *item, const OptionItem *opt, Core::Settings &settings) {
@@ -792,7 +835,17 @@ struct Inventory {
game->playSound(TR::SND_PISTOLS_SHOT);
}
if (opt->title == STR_APPLY) {
if (item->type == TR::Entity::INV_CONTROLS && opt->type == OptionItem::TYPE_KEY) {
waitForKey = opt;
Input::lastKey = ikNone;
Input::joy[Core::settings.controls[Core::settings.playerIndex].joyIndex].lastKey = jkNone;
}
if (item->type == TR::Entity::INV_SOUND || item->type == TR::Entity::INV_CONTROLS) {
game->applySettings(settings);
}
if (item->type == TR::Entity::INV_DETAIL && opt->title == STR_APPLY) {
game->applySettings(settings);
chosen = false;
}
@@ -834,17 +887,17 @@ struct Inventory {
Input::Joystick &joy = Input::joy[Core::settings.controls[playerIndex].joyIndex];
ControlKey key = cMAX;
if (Input::state[playerIndex][cAction])
if (Input::down[ikCtrl] || Input::down[ikEnter] || joy.down[jkA])
key = cAction;
else if (Input::state[playerIndex][cInventory] || Input::state[playerIndex][cJump])
else if (Input::down[ikAlt] || joy.down[jkB] || Input::state[playerIndex][cInventory])
key = cInventory;
else if (Input::state[playerIndex][cLeft] || joy.L.x < -0.5f)
else if (Input::down[ikLeft] || joy.down[jkLeft] || joy.L.x < -0.5f)
key = cLeft;
else if (Input::state[playerIndex][cRight] || joy.L.x > 0.5f)
else if (Input::down[ikRight] || joy.down[jkRight] || joy.L.x > 0.5f)
key = cRight;
else if (Input::state[playerIndex][cUp] || joy.L.y < -0.5f)
else if (Input::down[ikUp] || joy.down[jkUp] || joy.L.y < -0.5f)
key = cUp;
else if (Input::state[playerIndex][cDown] || joy.L.y > 0.5f)
else if (Input::down[ikDown] || joy.down[jkDown] || joy.L.y > 0.5f)
key = cDown;
Item *item = items[getGlobalIndex(page, index)];
@@ -876,8 +929,40 @@ struct Inventory {
}
}
if (key != cMAX && lastKey != key && changeTimer == 0.0f && phaseChoose == 1.0f)
if (waitForKey) {
int newKey = -1;
if (Core::settings.ctrlIndex == 0 && Input::lastKey != ikNone) {
newKey = Input::lastKey;
} else {
JoyKey jk = Input::joy[Core::settings.controls[Core::settings.playerIndex].joyIndex].lastKey;
if (Core::settings.ctrlIndex == 1 && jk != jkNone)
newKey = jk;
}
if (newKey != -1) {
waitForKey->setValue(newKey, &Core::settings);
waitForKey = NULL;
lastKey = key;
game->applySettings(Core::settings);
}
}
if (key != cMAX && lastKey != key && changeTimer == 0.0f && phaseChoose == 1.0f) {
controlItem(item, key);
}
}
if ((key == cInventory || key == cJump) && lastKey != key) {
lastKey = key;
if (chosen) {
if (phaseChoose == 1.0f) {
chosen = false;
item->anim->dir = 1.0f;
item->value = 1000;
item->angle = 0.0f;
}
} else
toggle();
}
}
lastKey = key;
@@ -1036,7 +1121,7 @@ struct Inventory {
sprintf(buf, "%d %c", item->count, spec);
for (int i = 0; buf[i] != ' '; i++)
buf[i] -= 47;
UI::textOut(pos, buf, UI::aRight, width, UI::SHADE_NONE);
UI::textOut(pos, buf, UI::aRight, width, 255, UI::SHADE_NONE);
}
}
@@ -1270,13 +1355,13 @@ struct Inventory {
UI::textOut(vec2(-eye, 32), pageTitle[page], UI::aCenter, UI::width);
if (canFlipPage(-1)) {
UI::textOut(vec2(16 - eye, 32), "[", UI::aLeft, UI::width, UI::SHADE_NONE);
UI::textOut(vec2(-eye, 32), "[", UI::aRight, UI::width - 20, UI::SHADE_NONE);
UI::textOut(vec2(16 - eye, 32), "[", UI::aLeft, UI::width);
UI::textOut(vec2(-eye, 32), "[", UI::aRight, UI::width - 20);
}
if (canFlipPage(1)) {
UI::textOut(vec2(16 - eye, 480 - 16), "]", UI::aLeft, UI::width, UI::SHADE_NONE);
UI::textOut(vec2(-eye, 480 - 16), "]", UI::aRight, UI::width - 20, UI::SHADE_NONE);
UI::textOut(vec2(16 - eye, 480 - 16), "]", UI::aLeft, UI::width);
UI::textOut(vec2(-eye, 480 - 16), "]", UI::aRight, UI::width - 20);
}
if (index == targetIndex)
@@ -1284,15 +1369,13 @@ struct Inventory {
// inventory controls help
float dx = 32.0f - eye;
UI::textOut(vec2(dx, 480 - 64), "Ctrl - Select", UI::aLeft, UI::width, UI::SHADE_NONE);
if (chosen)
UI::textOut(vec2(0, 480 - 64),
#ifdef __EMSCRIPTEN__
"D"
#else
"Alt"
#endif
" - Go Back", UI::aRight, UI::width - dx, UI::SHADE_NONE);
char buf[64];
sprintf(buf, STR[STR_HELP_SELECT], STR[STR_KEY_FIRST + ikEnter] );
UI::textOut(vec2(dx, 480 - 64), buf, UI::aLeft, UI::width);
if (chosen) {
sprintf(buf, STR[STR_HELP_BACK], STR[STR_KEY_FIRST + Core::settings.controls[playerIndex].keys[ cInventory ].key] );
UI::textOut(vec2(0, 480 - 64), buf, UI::aRight, UI::width - dx);
}
}
};

View File

@@ -253,6 +253,7 @@ struct Lara : Character {
Camera *camera;
float hitTimer;
bool camChanged; // hit key detection to go first person view mode
#ifdef _DEBUG
//uint16 *dbgBoxes;
@@ -431,7 +432,8 @@ struct Lara : Character {
Lara(IGame *game, int entity) : Character(game, entity, LARA_MAX_HEALTH), dozy(false), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), braid(NULL) {
camera = new Camera(game, this);
hitTimer = 0.0f;
hitTimer = 0.0f;
camChanged = false;
if (level->extra.laraSkin > -1)
level->entities[entity].modelIndex = level->extra.laraSkin + 1;
@@ -2652,6 +2654,14 @@ struct Lara : Character {
// analog control
rotFactor = vec2(1.0f);
if ((input & LOOK) && (input & ACTION)) {
if (!camChanged) {
camera->changeView(!camera->firstPerson);
camChanged = true;
}
} else
camChanged = false;
if (input & LOOK)
return input;
@@ -2700,7 +2710,14 @@ struct Lara : Character {
|| state == STATE_USE_KEY
|| state == STATE_USE_PUZZLE
|| state == STATE_SPECIAL
|| state == STATE_REACH;
|| state == STATE_REACH
|| state == STATE_SWAN_DIVE
|| state == STATE_HANDSTAND
|| state == STATE_ROLL_1
|| state == STATE_ROLL_2
|| animation.index == ANIM_CLIMB_2
|| animation.index == ANIM_CLIMB_3
|| animation.index == ANIM_CLIMB_JUMP;
}
virtual void doCustomCommand(int curFrame, int prevFrame) {
@@ -2749,9 +2766,9 @@ struct Lara : Character {
if (hitTimer > 0.0f) {
hitTimer -= Core::deltaTime;
if (hitTimer > 0.0f)
osJoyVibrate(Core::settings.controls[camera->cameraIndex].joyIndex, 0.5f, 0.5f);
Input::setJoyVibrate(camera->cameraIndex, 0.5f, 0.5f);
else
osJoyVibrate(Core::settings.controls[camera->cameraIndex].joyIndex, 0, 0);
Input::setJoyVibrate(camera->cameraIndex, 0, 0);
}
if (level->isCutsceneLevel())

View File

@@ -130,21 +130,24 @@ struct Level : IGame {
save->size = ptr - data;
save->version = level.version & TR::VER_VERSION;
Stream::write("savegame.dat", data, int(ptr - data));
osSaveGame(data, int(ptr - data));
delete[] data;
LOG("Ok\n");
}
virtual void loadGame(int slot) {
LOG("Lave Game... ");
LOG("Load Game... ");
Stream *stream = osLoadGame();
if (!stream)
return;
clearInventory();
clearEntities();
Stream stream("savegame.dat");
char *data;
stream.read(data, stream.size);
stream->read(data, stream->size);
char *ptr = data;
TR::SaveGame *save = (TR::SaveGame*)ptr;
@@ -188,6 +191,7 @@ struct Level : IGame {
}
delete[] data;
delete stream;
// camera->room = lara->getRoomIndex();
// camera->pos = camera->destPos = lara->pos;
@@ -233,6 +237,8 @@ struct Level : IGame {
Core::settings = settings;
osCacheWrite("settings", (char*)&settings, sizeof(settings));
if (rebuildShaders) {
delete shaderCache;
shaderCache = new ShaderCache();
@@ -516,7 +522,7 @@ struct Level : IGame {
switch (b.flags.mode) {
case 0 : if (level.version & TR::VER_TR1) flags |= Sound::UNIQUE; break; // TODO check this
case 1 : flags |= Sound::REPLAY; break;
case 2 : if (level.version & TR::VER_TR1) flags |= Sound::FLIPPED | Sound::UNFLIPPED | Sound::LOOP | Sound::UNIQUE; break;
case 2 : if (level.version & TR::VER_TR1) flags |= Sound::FLIPPED | Sound::UNFLIPPED | Sound::LOOP; break;
case 3 : if (!(level.version & TR::VER_TR1)) flags |= Sound::FLIPPED | Sound::UNFLIPPED | Sound::LOOP | Sound::UNIQUE; break;
}
}
@@ -549,7 +555,6 @@ struct Level : IGame {
}
level->sndSoundtrack->setVolume(1.0f, 0.2f);
}
LOG("play soundtrack - %d\n", Core::getTime());
}
virtual void playTrack(uint8 track, bool restart = false) {
@@ -1408,7 +1413,7 @@ struct Level : IGame {
}
}
if ((Input::state[0][cInventory] || Input::state[1][cInventory]) && !level.isTitle() && inventory.titleTimer < 1.0f) {
if ((Input::state[0][cInventory] || Input::state[1][cInventory]) && !level.isTitle() && inventory.titleTimer < 1.0f && !inventory.active && inventory.lastKey == cMAX) {
int playerIndex = Input::state[0][cInventory] ? 0 : 1;
if (player->health <= 0.0f)

View File

@@ -553,8 +553,8 @@ struct MeshBuilder {
m.geometry[1].ranges[0].iCount = 0;
m.geometry[2].ranges[0].iCount = 0;
// remove bottom triangles from skybox
if (m.geometry[0].ranges[0].iCount && ((level.version & TR::VER_TR3)))
m.geometry[0].ranges[0].iCount -= 16 * 3;
//if (m.geometry[0].ranges[0].iCount && ((level.version & TR::VER_TR3)))
// m.geometry[0].ranges[0].iCount -= 16 * 3;
}
}
ASSERT(vCount - vStartModel <= 0xFFFF);
@@ -1184,9 +1184,9 @@ struct MeshBuilder {
Vertex &v = vertices[vCount + i];
v.normal = short4( 0, 0, 0, 0 );
if (color2 != 0 && (i == 0 || i == 3))
v.color = *((ubyte4*)&color2);
v.light = *((ubyte4*)&color2);
else
v.color = *((ubyte4*)&color);
v.light = *((ubyte4*)&color);
short2 uv = tile.texCoord[i];
@@ -1218,7 +1218,7 @@ struct MeshBuilder {
for (int i = 0; i < 8; i++) {
Vertex &v = vertices[vCount + i];
v.normal = short4( 0, 0, 0, 0 );
v.color = *((ubyte4*)&color1);
v.light = *((ubyte4*)&color1);
v.texCoord = uv;
}
@@ -1238,7 +1238,7 @@ struct MeshBuilder {
for (int i = 0; i < 8; i++) {
Vertex &v = vertices[vCount + i];
v.normal = short4( 0, 0, 0, 0 );
v.color = *((ubyte4*)&color2);
v.light = *((ubyte4*)&color2);
v.texCoord = uv;
}

View File

@@ -112,14 +112,34 @@ int osGetTime() {
#endif
}
bool osSave(const char *name, const void *data, int size) {
FILE *f = fopen(name, "wb");
bool osCacheWrite(const char *name, const char *data, int size) {
char path[255];
strcpy(path, Stream::cacheDir);
strcat(path, name);
FILE *f = fopen(path, "wb");
if (!f) return false;
fwrite(data, size, 1, f);
fclose(f);
return true;
}
Stream* osCacheRead(const char *name) {
char path[255];
strcpy(path, Stream::cacheDir);
strcat(path, name);
if (!Stream::exists(path))
return NULL;
return new Stream(path);
}
bool osSaveGame(const char *data, int size) {
return osCacheWrite("savegame", data, size);
}
Stream* osLoadGame() {
return osCacheRead("savegame");
}
// common input functions
InputKey keyToInputKey(int code) {
static const int codes[] = {
@@ -168,8 +188,10 @@ typedef struct _XINPUT_VIBRATION
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30
DWORD (WINAPI *XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
DWORD (WINAPI *XInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
DWORD (WINAPI *XInputGetState) (DWORD dwUserIndex, XINPUT_STATE* pState) = NULL;
DWORD (WINAPI *XInputSetState) (DWORD dwUserIndex, XINPUT_VIBRATION* pVibration) = NULL;
void (WINAPI *XInputEnable) (BOOL enable) = NULL;
#define XInputGetProc(x) (x = (decltype(x))GetProcAddress(h, #x))
#define JOY_DEAD_ZONE_STICK 0.3f
#define JOY_DEAD_ZONE_TRIGGER 0.01f
@@ -193,9 +215,12 @@ void joyInit() {
memset(joyReady, 0, sizeof(joyReady));
HMODULE h = LoadLibrary("xinput1_3.dll");
if (h == NULL)
h = LoadLibrary("xinput9_1_0.dll");
XInputGetState = (decltype(XInputGetState))GetProcAddress(h, "XInputGetState");
XInputSetState = (decltype(XInputSetState))GetProcAddress(h, "XInputSetState");
XInputGetProc(XInputGetState);
XInputGetProc(XInputSetState);
XInputGetProc(XInputEnable);
for (int j = 0; j < INPUT_JOY_COUNT; j++) {
if (XInputGetState) { // XInput
@@ -403,6 +428,8 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPara
switch (msg) {
// window
case WM_ACTIVATE :
if (XInputEnable)
XInputEnable(wParam != WA_INACTIVE);
Input::reset();
break;
case WM_SIZE:

View File

@@ -80,7 +80,7 @@ struct Shader {
// generate shader file path
if (Core::support.shaderBinary) {
uint32 hash = fnv32(defines, strlen(defines), fnv32(source, strlen(source)));
sprintf(fileName, "%s%08X.xsh", Stream::cacheDir, hash);
sprintf(fileName, "%08X.xsh", hash);
}
ID = glCreateProgram();
@@ -94,7 +94,7 @@ struct Shader {
glGetProgramBinary(ID, size, NULL, &format, &data[8]);
*(int*)(&data[0]) = format;
*(int*)(&data[4]) = size;
Stream::write(fileName, data, 8 + size);
osCacheWrite(fileName, data, 8 + size);
delete[] data;
#endif
}
@@ -145,17 +145,18 @@ struct Shader {
}
bool linkBinary(const char *fileName) {
if (!Stream::exists(fileName))
Stream *stream = osCacheRead(fileName);
if (!stream)
return false;
GLenum size, format;
Stream stream(fileName);
stream.read(format);
stream.read(size);
stream->read(format);
stream->read(size);
char *data = new char[size];
stream.raw(data, size);
stream->raw(data, size);
glProgramBinary(ID, format, data, size);
delete[] data;
delete stream;
return checkLink();
}

View File

@@ -14,11 +14,11 @@ varying vec4 vColor;
attribute vec4 aCoord;
attribute vec4 aTexCoord;
attribute vec4 aColor;
attribute vec4 aLight;
void main() {
vTexCoord = aTexCoord.xy;
vColor = aColor;
vColor = aLight;
gl_Position = uViewProj * vec4(aCoord.xy * uPosScale.zw + uPosScale.xy, 0.0, 1.0);
}
#else

View File

@@ -24,6 +24,9 @@ enum StringID {
, STR_NOT_READY
, STR_PLAYER_1
, STR_PLAYER_2
, STR_PRESS_ANY_KEY
, STR_HELP_SELECT
, STR_HELP_BACK
// inventory pages
, STR_OPTION
, STR_INVENTORY
@@ -59,10 +62,20 @@ enum StringID {
, STR_REVERBERATION
// controls options
, STR_SET_CONTROLS
, STR_OPT_CONTROLS_KEYBOARD
, STR_OPT_CONTROLS_GAMEPAD
, STR_OPT_CONTROLS_VIBRATION
, STR_OPT_CONTROLS_RETARGET
, STR_OPT_CONTROLS_MULTIAIM
// controls
, STR_CTRL_FIRST
, STR_CTRL_LAST = STR_CTRL_FIRST + cMAX - 1
// keys
, STR_KEY_FIRST
, STR_KEY_LAST = STR_KEY_FIRST + ikZ
// gamepad
, STR_JOY_FIRST
, STR_JOY_LAST = STR_JOY_FIRST + jkMAX - 1
// inventory items
, STR_UNKNOWN
, STR_PISTOLS
@@ -83,32 +96,21 @@ enum StringID {
};
const char *helpText =
"Controls gamepad, touch and keyboard:@"
" Enter, Start (gamepad) - add second player or restore Lara@"
" H - Show or hide this help@"
" TAB - Inventory@"
" LEFT - Left@"
" RIGHT - Right@"
" UP - Run@"
" DOWN - Back@"
" SHIFT - Walk@"
" SPACE - Draw Weapon@"
" CTRL - Action@"
" D - Jump@"
" Z - Step Left@"
" X - Step Right@"
" A - Roll@"
" C - Look # not implemented #@"
" V - First Person View@"
" R - slow motion@"
" T - fast motion@"
" ALT + ENTER - Fullscreen@@"
"Actions:@"
" Out of water - Run + Action@"
" Handstand - Run + Walk@"
" Swan dive - Run + Walk + jump@"
" DOZY on - Look + Step Right + Action + Jump@"
" DOZY off - Walk@";
"Start - add second player or restore Lara@"
"H - Show or hide this help@"
"ALT + ENTER - Fullscreen@"
"C - Look@"
"R - Slow motion@"
"T - Fast motion@"
"Roll - Up + Down@"
"Step Left - Walk + Left@"
"Step Right - Walk + Right@"
"Out of water - Up + Action@"
"Handstand - Run + Walk@"
"Swan dive - Run + Walk + Jump@"
"First Person View - Look + Action@"
"DOZY on - Look + Duck + Action + Jump@"
"DOZY off - Walk";
const char *STR[STR_MAX] = {
@@ -131,6 +133,9 @@ const char *STR[STR_MAX] = {
, "Not Ready"
, "Player 1"
, "Player 2"
, "Press Any Key"
, "%s - Select"
, "%s - Go Back"
// inventory pages
, "OPTION"
, "INVENTORY"
@@ -166,10 +171,20 @@ const char *STR[STR_MAX] = {
, "Reverberation"
// controls options
, "Set Controls"
, "Keyboard"
, "Gamepad"
, "Vibration"
, "Retargeting"
, "Multi-aiming"
// controls
, "Left", "Right", "Up", "Down", "Jump", "Walk", "Action", "Draw Weapon", "Look", "Duck", "Dash", "Roll", "Inventory", "Start"
// keys
, "NONE", "LEFT", "RIGHT", "UP", "DOWN", "SPACE", "TAB", "ENTER", "ESCAPE", "SHIFT", "CTRL", "ALT"
, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
, "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"
, "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
// gamepad
, "NONE", "A", "B", "X", "Y", "L BUMPER", "R BUMPER", "SELECT", "START", "L STICK", "R STICK", "L TRIGGER", "R TRIGGER", "D-LEFT", "D-RIGHT", "D-UP", "D-DOWN"
// inventory items
, "Unknown"
, "Pistols"
@@ -311,13 +326,13 @@ namespace UI {
SHADE_GRAY = 2,
};
void textOut(const vec2 &pos, const char *text, Align align = aLeft, float width = 0, ShadeType shade = SHADE_ORANGE, bool isShadow = false) {
void textOut(const vec2 &pos, const char *text, Align align = aLeft, float width = 0, uint8 alpha = 255, ShadeType shade = SHADE_ORANGE, bool isShadow = false) {
if (!text) return;
TR::Level *level = game->getLevel();
if (shade && !isShadow && ((level->version & TR::VER_TR3)))
textOut(pos + vec2(1, 1), text, align, width, shade, true);
textOut(pos + vec2(1, 1), text, align, width, alpha, shade, true);
MeshBuilder *mesh = game->getMesh();
int seq = level->extra.glyphs;
@@ -354,18 +369,18 @@ namespace UI {
TR::Color32 tColor, bColor;
if (isShadow) {
tColor = bColor = TR::Color32(0, 0, 0, 255);
tColor = bColor = TR::Color32(0, 0, 0, alpha);
} else {
tColor = bColor = TR::Color32(255, 255, 255, 255);
tColor = bColor = TR::Color32(255, 255, 255, alpha);
if (shade && ((level->version & TR::VER_TR3))) {
if (shade == SHADE_ORANGE) {
tColor = TR::Color32(255, 190, 90, 255);
bColor = TR::Color32(140, 50, 10, 255);
tColor = TR::Color32(255, 190, 90, alpha);
bColor = TR::Color32(140, 50, 10, alpha);
}
if (shade == SHADE_GRAY) {
tColor = TR::Color32(255, 255, 255, 255);
bColor = TR::Color32(128, 128, 128, 255);
tColor = TR::Color32(255, 255, 255, alpha);
bColor = TR::Color32(128, 128, 128, alpha);
}
}
}
@@ -389,8 +404,8 @@ namespace UI {
}
}
void textOut(const vec2 &pos, StringID str, Align align = aLeft, float width = 0, ShadeType shade = SHADE_ORANGE) {
textOut(pos, STR[str], align, width, shade);
void textOut(const vec2 &pos, StringID str, Align align = aLeft, float width = 0, uint8 alpha = 255, ShadeType shade = SHADE_ORANGE) {
textOut(pos, STR[str], align, width, alpha, shade);
}
void specOut(const vec2 &pos, char specChar) {
@@ -508,10 +523,10 @@ namespace UI {
void renderHelp() {
// TODO: Core::eye offset
if (showHelp)
textOut(vec2(0, 32), STR_HELP_TEXT, aRight, width - 32, UI::SHADE_GRAY);
textOut(vec2(32, 32), STR_HELP_TEXT, aLeft, width - 32, 255, UI::SHADE_GRAY);
else
if (helpTipTime > 0.0f)
textOut(vec2(0, height - 32), STR_HELP_PRESS, aCenter, width, UI::SHADE_ORANGE);
textOut(vec2(0, height - 32), STR_HELP_PRESS, aCenter, width, 255, UI::SHADE_ORANGE);
}
};

View File

@@ -1211,10 +1211,6 @@ struct Stream {
return exists(fileName);
}
static void write(const char *name, const void *data, int size) {
osSave(name, data, size);
}
void setPos(int pos) {
this->pos = pos;
if (f) fseek(f, pos, SEEK_SET);