2016-03-20 17:00:00 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* REminiscence - Flashback interpreter
|
2018-01-20 17:00:00 +01:00
|
|
|
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net)
|
2015-08-02 18:00:00 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cutscene.h"
|
2016-03-20 17:00:00 +01:00
|
|
|
#include "game.h"
|
2015-08-02 18:00:00 +02:00
|
|
|
#include "resource.h"
|
|
|
|
#include "systemstub.h"
|
2016-03-20 17:00:00 +01:00
|
|
|
#include "util.h"
|
2015-08-02 18:00:00 +02:00
|
|
|
|
|
|
|
void Game::pge_resetGroups() {
|
|
|
|
memset(_pge_groupsTable, 0, sizeof(_pge_groupsTable));
|
|
|
|
GroupPGE *le = &_pge_groups[0];
|
|
|
|
_pge_nextFreeGroup = le;
|
|
|
|
int n = 0xFF;
|
|
|
|
while (n--) {
|
|
|
|
le->next_entry = le + 1;
|
|
|
|
le->index = 0;
|
|
|
|
le->group_id = 0;
|
|
|
|
++le;
|
|
|
|
}
|
|
|
|
le->next_entry = 0;
|
|
|
|
le->index = 0;
|
|
|
|
le->group_id = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_removeFromGroup(uint8_t idx) {
|
|
|
|
GroupPGE *le = _pge_groupsTable[idx];
|
|
|
|
if (le) {
|
|
|
|
_pge_groupsTable[idx] = 0;
|
|
|
|
GroupPGE *next = _pge_nextFreeGroup;
|
|
|
|
while (le) {
|
|
|
|
GroupPGE *cur = le->next_entry;
|
|
|
|
le->next_entry = next;
|
|
|
|
le->index = 0;
|
|
|
|
le->group_id = 0;
|
|
|
|
next = le;
|
|
|
|
le = cur;
|
|
|
|
}
|
|
|
|
_pge_nextFreeGroup = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_isInGroup(LivePGE *pge_dst, uint16_t group_id, uint16_t counter) {
|
|
|
|
assert(counter >= 1 && counter <= 4);
|
|
|
|
uint16_t c = pge_dst->init_PGE->counter_values[counter - 1];
|
|
|
|
GroupPGE *le = _pge_groupsTable[pge_dst->index];
|
|
|
|
while (le) {
|
|
|
|
if (le->group_id == group_id && le->index == c)
|
|
|
|
return 1;
|
|
|
|
le = le->next_entry;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_loadForCurrentLevel(uint16_t idx) {
|
|
|
|
debug(DBG_PGE, "Game::pge_loadForCurrentLevel() idx=%d", idx);
|
|
|
|
|
|
|
|
LivePGE *live_pge = &_pgeLive[idx];
|
|
|
|
InitPGE *init_pge = &_res._pgeInit[idx];
|
|
|
|
|
|
|
|
live_pge->init_PGE = init_pge;
|
|
|
|
live_pge->obj_type = init_pge->type;
|
|
|
|
live_pge->pos_x = init_pge->pos_x;
|
|
|
|
live_pge->pos_y = init_pge->pos_y;
|
|
|
|
live_pge->anim_seq = 0;
|
|
|
|
live_pge->room_location = init_pge->init_room;
|
|
|
|
|
|
|
|
live_pge->life = init_pge->life;
|
|
|
|
if (_skillLevel >= 2 && init_pge->object_type == 10) {
|
|
|
|
live_pge->life *= 2;
|
|
|
|
}
|
|
|
|
live_pge->counter_value = 0;
|
|
|
|
live_pge->collision_slot = 0xFF;
|
|
|
|
live_pge->next_inventory_PGE = 0xFF;
|
|
|
|
live_pge->current_inventory_PGE = 0xFF;
|
|
|
|
live_pge->unkF = 0xFF;
|
|
|
|
live_pge->anim_number = 0;
|
|
|
|
live_pge->index = idx;
|
|
|
|
live_pge->next_PGE_in_room = 0;
|
|
|
|
|
|
|
|
uint16_t flags = 0;
|
|
|
|
if (init_pge->skill <= _skillLevel) {
|
|
|
|
if (init_pge->room_location != 0 || ((init_pge->flags & 4) && (_currentRoom == init_pge->init_room))) {
|
|
|
|
flags |= 4;
|
|
|
|
_pge_liveTable2[idx] = live_pge;
|
|
|
|
}
|
|
|
|
if (init_pge->mirror_x != 0) {
|
|
|
|
flags |= 1;
|
|
|
|
}
|
|
|
|
if (init_pge->init_flags & 8) {
|
|
|
|
flags |= 0x10;
|
|
|
|
}
|
|
|
|
flags |= (init_pge->init_flags & 3) << 5;
|
|
|
|
if (init_pge->flags & 2) {
|
|
|
|
flags |= 0x80;
|
|
|
|
}
|
|
|
|
live_pge->flags = flags;
|
|
|
|
assert(init_pge->obj_node_number < _res._numObjectNodes);
|
|
|
|
ObjectNode *on = _res._objectNodesMap[init_pge->obj_node_number];
|
|
|
|
Object *obj = on->objects;
|
|
|
|
int i = 0;
|
|
|
|
while (obj->type != live_pge->obj_type) {
|
|
|
|
++i;
|
|
|
|
++obj;
|
|
|
|
}
|
|
|
|
assert(i < on->num_objects);
|
|
|
|
live_pge->first_obj_number = i;
|
|
|
|
pge_setupDefaultAnim(live_pge);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_process(LivePGE *pge) {
|
2016-08-07 18:00:00 +02:00
|
|
|
debug(DBG_PGE, "Game::pge_process() pge_num=%ld", pge - &_pgeLive[0]);
|
2015-08-02 18:00:00 +02:00
|
|
|
_pge_playAnimSound = true;
|
|
|
|
_pge_currentPiegeFacingDir = (pge->flags & 1) != 0;
|
|
|
|
_pge_currentPiegeRoom = pge->room_location;
|
|
|
|
GroupPGE *le = _pge_groupsTable[pge->index];
|
|
|
|
if (le) {
|
|
|
|
pge_setupNextAnimFrame(pge, le);
|
|
|
|
}
|
|
|
|
const uint8_t *anim_data = _res.getAniData(pge->obj_type);
|
2016-03-20 17:00:00 +01:00
|
|
|
if (_res._readUint16(anim_data) <= pge->anim_seq) {
|
2015-08-02 18:00:00 +02:00
|
|
|
InitPGE *init_pge = pge->init_PGE;
|
|
|
|
assert(init_pge->obj_node_number < _res._numObjectNodes);
|
|
|
|
ObjectNode *on = _res._objectNodesMap[init_pge->obj_node_number];
|
|
|
|
Object *obj = &on->objects[pge->first_obj_number];
|
|
|
|
while (1) {
|
|
|
|
if (obj->type != pge->obj_type) {
|
|
|
|
pge_removeFromGroup(pge->index);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
uint16_t _ax = pge_execute(pge, init_pge, obj);
|
|
|
|
if (_ax != 0) {
|
|
|
|
anim_data = _res.getAniData(pge->obj_type);
|
|
|
|
uint8_t snd = anim_data[2];
|
|
|
|
if (snd) {
|
|
|
|
pge_playAnimSound(pge, snd);
|
|
|
|
}
|
|
|
|
pge_setupOtherPieges(pge, init_pge);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++obj;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pge_setupAnim(pge);
|
|
|
|
++pge->anim_seq;
|
|
|
|
pge_removeFromGroup(pge->index);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_setupNextAnimFrame(LivePGE *pge, GroupPGE *le) {
|
|
|
|
InitPGE *init_pge = pge->init_PGE;
|
|
|
|
assert(init_pge->obj_node_number < _res._numObjectNodes);
|
|
|
|
ObjectNode *on = _res._objectNodesMap[init_pge->obj_node_number];
|
|
|
|
Object *obj = &on->objects[pge->first_obj_number];
|
|
|
|
int i = pge->first_obj_number;
|
|
|
|
while (i < on->last_obj_number && pge->obj_type == obj->type) {
|
|
|
|
GroupPGE *next_le = le;
|
|
|
|
while (next_le) {
|
|
|
|
uint16_t groupId = next_le->group_id;
|
|
|
|
if (obj->opcode2 == 0x6B) { // pge_op_isInGroupSlice
|
|
|
|
if (obj->opcode_arg2 == 0) {
|
|
|
|
if (groupId == 1 || groupId == 2) goto set_anim;
|
|
|
|
}
|
|
|
|
if (obj->opcode_arg2 == 1) {
|
|
|
|
if (groupId == 3 || groupId == 4) goto set_anim;
|
|
|
|
}
|
|
|
|
} else if (groupId == obj->opcode_arg2) {
|
|
|
|
if (obj->opcode2 == 0x22 || obj->opcode2 == 0x6F) goto set_anim;
|
|
|
|
}
|
|
|
|
if (obj->opcode1 == 0x6B) { // pge_op_isInGroupSlice
|
|
|
|
if (obj->opcode_arg1 == 0) {
|
|
|
|
if (groupId == 1 || groupId == 2) goto set_anim;
|
|
|
|
}
|
|
|
|
if (obj->opcode_arg1 == 1) {
|
|
|
|
if (groupId == 3 || groupId == 4) goto set_anim;
|
|
|
|
}
|
|
|
|
} else if (groupId == obj->opcode_arg1) {
|
|
|
|
if (obj->opcode1 == 0x22 || obj->opcode1 == 0x6F) goto set_anim;
|
|
|
|
}
|
|
|
|
next_le = next_le->next_entry;
|
|
|
|
}
|
|
|
|
++obj;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
set_anim:
|
|
|
|
const uint8_t *anim_data = _res.getAniData(pge->obj_type);
|
2016-03-20 17:00:00 +01:00
|
|
|
uint8_t _dh = _res._readUint16(anim_data);
|
2015-08-02 18:00:00 +02:00
|
|
|
uint8_t _dl = pge->anim_seq;
|
|
|
|
const uint8_t *anim_frame = anim_data + 6 + _dl * 4;
|
|
|
|
while (_dh > _dl) {
|
2018-01-14 17:00:00 +01:00
|
|
|
if (_res._readUint16(anim_frame) != 0xFFFF) {
|
2015-08-02 18:00:00 +02:00
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
pge->pos_x -= (int8_t)anim_frame[2];
|
|
|
|
} else {
|
|
|
|
pge->pos_x += (int8_t)anim_frame[2];
|
|
|
|
}
|
|
|
|
pge->pos_y += (int8_t)anim_frame[3];
|
|
|
|
}
|
|
|
|
anim_frame += 4;
|
|
|
|
++_dl;
|
|
|
|
}
|
|
|
|
pge->anim_seq = _dh;
|
|
|
|
_col_currentPiegeGridPosY = (pge->pos_y / 36) & ~1;
|
|
|
|
_col_currentPiegeGridPosX = (pge->pos_x + 8) >> 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_playAnimSound(LivePGE *pge, uint16_t arg2) {
|
|
|
|
if ((pge->flags & 4) && _pge_playAnimSound) {
|
|
|
|
uint8_t sfxId = (arg2 & 0xFF) - 1;
|
|
|
|
if (_currentRoom == pge->room_location) {
|
|
|
|
playSound(sfxId, 0);
|
|
|
|
} else {
|
|
|
|
if (_res._ctData[CT_DOWN_ROOM + _currentRoom] == pge->room_location ||
|
|
|
|
_res._ctData[CT_UP_ROOM + _currentRoom] == pge->room_location ||
|
|
|
|
_res._ctData[CT_RIGHT_ROOM + _currentRoom] == pge->room_location ||
|
|
|
|
_res._ctData[CT_LEFT_ROOM + _currentRoom] == pge->room_location) {
|
|
|
|
playSound(sfxId, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_setupAnim(LivePGE *pge) {
|
2016-08-07 18:00:00 +02:00
|
|
|
debug(DBG_PGE, "Game::pge_setupAnim() pgeNum=%ld", pge - &_pgeLive[0]);
|
2015-08-02 18:00:00 +02:00
|
|
|
const uint8_t *anim_data = _res.getAniData(pge->obj_type);
|
2016-03-20 17:00:00 +01:00
|
|
|
if (_res._readUint16(anim_data) < pge->anim_seq) {
|
2015-08-02 18:00:00 +02:00
|
|
|
pge->anim_seq = 0;
|
|
|
|
}
|
|
|
|
const uint8_t *anim_frame = anim_data + 6 + pge->anim_seq * 4;
|
2016-03-20 17:00:00 +01:00
|
|
|
if (_res._readUint16(anim_frame) != 0xFFFF) {
|
|
|
|
uint16_t fl = _res._readUint16(anim_frame);
|
2015-08-02 18:00:00 +02:00
|
|
|
if (pge->flags & 1) {
|
|
|
|
fl ^= 0x8000;
|
|
|
|
pge->pos_x -= (int8_t)anim_frame[2];
|
|
|
|
} else {
|
|
|
|
pge->pos_x += (int8_t)anim_frame[2];
|
|
|
|
}
|
|
|
|
pge->pos_y += (int8_t)anim_frame[3];
|
|
|
|
pge->flags &= ~2;
|
|
|
|
if (fl & 0x8000) {
|
|
|
|
pge->flags |= 2;
|
|
|
|
}
|
|
|
|
pge->flags &= ~8;
|
2016-03-20 17:00:00 +01:00
|
|
|
if (_res._readUint16(anim_data + 4) & 0xFFFF) {
|
2015-08-02 18:00:00 +02:00
|
|
|
pge->flags |= 8;
|
|
|
|
}
|
2016-03-20 17:00:00 +01:00
|
|
|
pge->anim_number = _res._readUint16(anim_frame) & 0x7FFF;
|
2015-08-02 18:00:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_execute(LivePGE *live_pge, InitPGE *init_pge, const Object *obj) {
|
2016-08-07 18:00:00 +02:00
|
|
|
debug(DBG_PGE, "Game::pge_execute() pge_num=%ld op1=0x%X op2=0x%X op3=0x%X", live_pge - &_pgeLive[0], obj->opcode1, obj->opcode2, obj->opcode3);
|
2015-08-02 18:00:00 +02:00
|
|
|
pge_OpcodeProc op;
|
|
|
|
ObjectOpcodeArgs args;
|
|
|
|
if (obj->opcode1) {
|
|
|
|
args.pge = live_pge;
|
|
|
|
args.a = obj->opcode_arg1;
|
|
|
|
args.b = 0;
|
|
|
|
debug(DBG_PGE, "pge_execute op1=0x%X", obj->opcode1);
|
|
|
|
op = _pge_opcodeTable[obj->opcode1];
|
|
|
|
if (!op) {
|
|
|
|
warning("Game::pge_execute() missing call to pge_opcode 0x%X", obj->opcode1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!((this->*op)(&args) & 0xFF))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (obj->opcode2) {
|
|
|
|
args.pge = live_pge;
|
|
|
|
args.a = obj->opcode_arg2;
|
|
|
|
args.b = obj->opcode_arg1;
|
|
|
|
debug(DBG_PGE, "pge_execute op2=0x%X", obj->opcode2);
|
|
|
|
op = _pge_opcodeTable[obj->opcode2];
|
|
|
|
if (!op) {
|
|
|
|
warning("Game::pge_execute() missing call to pge_opcode 0x%X", obj->opcode2);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!((this->*op)(&args) & 0xFF))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (obj->opcode3) {
|
|
|
|
args.pge = live_pge;
|
|
|
|
args.a = obj->opcode_arg3;
|
|
|
|
args.b = 0;
|
|
|
|
debug(DBG_PGE, "pge_execute op3=0x%X", obj->opcode3);
|
|
|
|
op = _pge_opcodeTable[obj->opcode3];
|
|
|
|
if (op) {
|
|
|
|
(this->*op)(&args);
|
|
|
|
} else {
|
|
|
|
warning("Game::pge_execute() missing call to pge_opcode 0x%X", obj->opcode3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
live_pge->obj_type = obj->init_obj_type;
|
|
|
|
live_pge->first_obj_number = obj->init_obj_number;
|
|
|
|
live_pge->anim_seq = 0;
|
|
|
|
if (obj->flags & 0xF0) {
|
|
|
|
_score += _scoreTable[obj->flags >> 4];
|
|
|
|
}
|
|
|
|
if (obj->flags & 1) {
|
|
|
|
live_pge->flags ^= 1;
|
|
|
|
}
|
|
|
|
if (obj->flags & 2) {
|
|
|
|
--live_pge->life;
|
|
|
|
if (init_pge->object_type == 1) {
|
|
|
|
_pge_processOBJ = true;
|
|
|
|
} else if (init_pge->object_type == 10) {
|
|
|
|
_score += 100;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (obj->flags & 4) {
|
|
|
|
++live_pge->life;
|
|
|
|
}
|
|
|
|
if (obj->flags & 8) {
|
|
|
|
live_pge->life = 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (live_pge->flags & 1) {
|
|
|
|
live_pge->pos_x -= obj->dx;
|
|
|
|
} else {
|
|
|
|
live_pge->pos_x += obj->dx;
|
|
|
|
}
|
|
|
|
live_pge->pos_y += obj->dy;
|
|
|
|
|
|
|
|
if (_pge_processOBJ) {
|
|
|
|
if (init_pge->object_type == 1) {
|
|
|
|
if (pge_processOBJ(live_pge) != 0) {
|
|
|
|
_blinkingConradCounter = 60;
|
|
|
|
_pge_processOBJ = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_prepare() {
|
|
|
|
col_clearState();
|
|
|
|
if (!(_currentRoom & 0x80)) {
|
|
|
|
LivePGE *pge = _pge_liveTable1[_currentRoom];
|
|
|
|
while (pge) {
|
|
|
|
col_preparePiegeState(pge);
|
|
|
|
if (!(pge->flags & 4) && (pge->init_PGE->flags & 4)) {
|
|
|
|
_pge_liveTable2[pge->index] = pge;
|
|
|
|
pge->flags |= 4;
|
|
|
|
}
|
|
|
|
pge = pge->next_PGE_in_room;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (uint16_t i = 0; i < _res._pgeNum; ++i) {
|
|
|
|
LivePGE *pge = _pge_liveTable2[i];
|
|
|
|
if (pge && _currentRoom != pge->room_location) {
|
|
|
|
col_preparePiegeState(pge);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_setupDefaultAnim(LivePGE *pge) {
|
|
|
|
const uint8_t *anim_data = _res.getAniData(pge->obj_type);
|
2016-03-20 17:00:00 +01:00
|
|
|
if (pge->anim_seq < _res._readUint16(anim_data)) {
|
2015-08-02 18:00:00 +02:00
|
|
|
pge->anim_seq = 0;
|
|
|
|
}
|
|
|
|
const uint8_t *anim_frame = anim_data + 6 + pge->anim_seq * 4;
|
2016-03-20 17:00:00 +01:00
|
|
|
if (_res._readUint16(anim_frame) != 0xFFFF) {
|
|
|
|
uint16_t f = _res._readUint16(anim_data);
|
2015-08-02 18:00:00 +02:00
|
|
|
if (pge->flags & 1) {
|
|
|
|
f ^= 0x8000;
|
|
|
|
}
|
|
|
|
pge->flags &= ~2;
|
|
|
|
if (f & 0x8000) {
|
|
|
|
pge->flags |= 2;
|
|
|
|
}
|
|
|
|
pge->flags &= ~8;
|
2016-03-20 17:00:00 +01:00
|
|
|
if (_res._readUint16(anim_data + 4) & 0xFFFF) {
|
2015-08-02 18:00:00 +02:00
|
|
|
pge->flags |= 8;
|
|
|
|
}
|
2016-03-20 17:00:00 +01:00
|
|
|
pge->anim_number = _res._readUint16(anim_frame) & 0x7FFF;
|
2016-08-07 18:00:00 +02:00
|
|
|
debug(DBG_PGE, "Game::pge_setupDefaultAnim() pgeNum=%ld pge->flags=0x%X pge->anim_number=0x%X pge->anim_seq=0x%X", pge - &_pgeLive[0], pge->flags, pge->anim_number, pge->anim_seq);
|
2015-08-02 18:00:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t Game::pge_processOBJ(LivePGE *pge) {
|
|
|
|
InitPGE *init_pge = pge->init_PGE;
|
|
|
|
assert(init_pge->obj_node_number < _res._numObjectNodes);
|
|
|
|
ObjectNode *on = _res._objectNodesMap[init_pge->obj_node_number];
|
|
|
|
Object *obj = &on->objects[pge->first_obj_number];
|
|
|
|
int i = pge->first_obj_number;
|
|
|
|
while (i < on->last_obj_number && pge->obj_type == obj->type) {
|
|
|
|
if (obj->opcode2 == 0x6B) return 0xFFFF;
|
|
|
|
if (obj->opcode2 == 0x22 && obj->opcode_arg2 <= 4) return 0xFFFF;
|
|
|
|
|
|
|
|
if (obj->opcode1 == 0x6B) return 0xFFFF;
|
|
|
|
if (obj->opcode1 == 0x22 && obj->opcode_arg1 <= 4) return 0xFFFF;
|
|
|
|
|
|
|
|
++obj;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_setupOtherPieges(LivePGE *pge, InitPGE *init_pge) {
|
|
|
|
const int8_t *room_ct_data = 0;
|
|
|
|
if (pge->pos_x <= -10) {
|
|
|
|
pge->pos_x += 256;
|
|
|
|
room_ct_data = &_res._ctData[CT_LEFT_ROOM];
|
|
|
|
} else if (pge->pos_x >= 256) {
|
|
|
|
pge->pos_x -= 256;
|
|
|
|
room_ct_data = &_res._ctData[CT_RIGHT_ROOM];
|
|
|
|
} else if (pge->pos_y < 0) {
|
|
|
|
pge->pos_y += 216;
|
|
|
|
room_ct_data = &_res._ctData[CT_UP_ROOM];
|
|
|
|
} else if (pge->pos_y >= 216) {
|
|
|
|
pge->pos_y -= 216;
|
|
|
|
room_ct_data = &_res._ctData[CT_DOWN_ROOM];
|
|
|
|
}
|
|
|
|
if (room_ct_data) {
|
|
|
|
int8_t room = pge->room_location;
|
|
|
|
if (room >= 0) {
|
|
|
|
room = room_ct_data[room];
|
|
|
|
pge->room_location = room;
|
|
|
|
}
|
|
|
|
if (init_pge->object_type == 1) {
|
|
|
|
_currentRoom = room;
|
|
|
|
col_prepareRoomState();
|
|
|
|
_loadMap = true;
|
|
|
|
if (!(_currentRoom & 0x80) && _currentRoom < 0x40) {
|
|
|
|
LivePGE *pge_it = _pge_liveTable1[_currentRoom];
|
|
|
|
while (pge_it) {
|
|
|
|
if (pge_it->init_PGE->flags & 4) {
|
|
|
|
_pge_liveTable2[pge_it->index] = pge_it;
|
|
|
|
pge_it->flags |= 4;
|
|
|
|
}
|
|
|
|
pge_it = pge_it->next_PGE_in_room;
|
|
|
|
}
|
|
|
|
room = _res._ctData[CT_UP_ROOM + _currentRoom];
|
|
|
|
if (room >= 0 && room < 0x40) {
|
|
|
|
pge_it = _pge_liveTable1[room];
|
|
|
|
while (pge_it) {
|
|
|
|
if (pge_it->init_PGE->object_type != 10 && pge_it->pos_y >= 48 && (pge_it->init_PGE->flags & 4)) {
|
|
|
|
_pge_liveTable2[pge_it->index] = pge_it;
|
|
|
|
pge_it->flags |= 4;
|
|
|
|
}
|
|
|
|
pge_it = pge_it->next_PGE_in_room;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
room = _res._ctData[CT_DOWN_ROOM + _currentRoom];
|
|
|
|
if (room >= 0 && room < 0x40) {
|
|
|
|
pge_it = _pge_liveTable1[room];
|
|
|
|
while (pge_it) {
|
|
|
|
if (pge_it->init_PGE->object_type != 10 && pge_it->pos_y >= 176 && (pge_it->init_PGE->flags & 4)) {
|
|
|
|
_pge_liveTable2[pge_it->index] = pge_it;
|
|
|
|
pge_it->flags |= 4;
|
|
|
|
}
|
|
|
|
pge_it = pge_it->next_PGE_in_room;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pge_addToCurrentRoomList(pge, _pge_currentPiegeRoom);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_addToCurrentRoomList(LivePGE *pge, uint8_t room) {
|
2016-08-07 18:00:00 +02:00
|
|
|
debug(DBG_PGE, "Game::pge_addToCurrentRoomList() pgeNum=%ld room=%d", pge - &_pgeLive[0], room);
|
2015-08-02 18:00:00 +02:00
|
|
|
if (room != pge->room_location) {
|
|
|
|
LivePGE *cur_pge = _pge_liveTable1[room];
|
|
|
|
LivePGE *prev_pge = 0;
|
|
|
|
while (cur_pge && cur_pge != pge) {
|
|
|
|
prev_pge = cur_pge;
|
|
|
|
cur_pge = cur_pge->next_PGE_in_room;
|
|
|
|
}
|
|
|
|
if (cur_pge) {
|
|
|
|
if (!prev_pge) {
|
|
|
|
_pge_liveTable1[room] = pge->next_PGE_in_room;
|
|
|
|
} else {
|
|
|
|
prev_pge->next_PGE_in_room = cur_pge->next_PGE_in_room;
|
|
|
|
}
|
|
|
|
LivePGE *temp = _pge_liveTable1[pge->room_location];
|
|
|
|
pge->next_PGE_in_room = temp;
|
|
|
|
_pge_liveTable1[pge->room_location] = pge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_getInput() {
|
|
|
|
inp_update();
|
|
|
|
_inp_lastKeysHit = _stub->_pi.dirMask;
|
|
|
|
if ((_inp_lastKeysHit & 0xC) && (_inp_lastKeysHit & 0x3)) {
|
|
|
|
const uint8_t mask = (_inp_lastKeysHit & 0xF0) | (_inp_lastKeysHitLeftRight & 0xF);
|
|
|
|
_pge_inpKeysMask = mask;
|
|
|
|
_inp_lastKeysHit = mask;
|
|
|
|
} else {
|
|
|
|
_pge_inpKeysMask = _inp_lastKeysHit;
|
|
|
|
_inp_lastKeysHitLeftRight = _inp_lastKeysHit;
|
|
|
|
}
|
|
|
|
if (_stub->_pi.enter) {
|
|
|
|
_pge_inpKeysMask |= 0x10;
|
|
|
|
}
|
|
|
|
if (_stub->_pi.space) {
|
|
|
|
_pge_inpKeysMask |= 0x20;
|
|
|
|
}
|
|
|
|
if (_stub->_pi.shift) {
|
|
|
|
_pge_inpKeysMask |= 0x40;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInpUp(ObjectOpcodeArgs *args) {
|
|
|
|
if (1 == _pge_inpKeysMask) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInpBackward(ObjectOpcodeArgs *args) {
|
|
|
|
uint8_t mask = 8; // right
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
mask = 4; // left
|
|
|
|
}
|
|
|
|
if (mask == _pge_inpKeysMask) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInpDown(ObjectOpcodeArgs *args) {
|
|
|
|
if (2 == _pge_inpKeysMask) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInpForward(ObjectOpcodeArgs *args) {
|
|
|
|
uint8_t mask = 4;
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
mask = 8;
|
|
|
|
}
|
|
|
|
if (mask == _pge_inpKeysMask) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInpUpMod(ObjectOpcodeArgs *args) {
|
|
|
|
assert(args->a < 3);
|
|
|
|
uint8_t mask = _pge_modKeysTable[args->a] | 1;
|
|
|
|
if (mask == _pge_inpKeysMask) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInpBackwardMod(ObjectOpcodeArgs *args) {
|
|
|
|
assert(args->a < 3);
|
|
|
|
uint8_t mask = _pge_modKeysTable[args->a];
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
mask |= 4;
|
|
|
|
} else {
|
|
|
|
mask |= 8;
|
|
|
|
}
|
|
|
|
if (mask == _pge_inpKeysMask) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInpDownMod(ObjectOpcodeArgs *args) {
|
|
|
|
assert(args->a < 3);
|
|
|
|
uint8_t mask = _pge_modKeysTable[args->a] | 2;
|
|
|
|
if (mask == _pge_inpKeysMask) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInpForwardMod(ObjectOpcodeArgs *args) {
|
|
|
|
assert(args->a < 3);
|
|
|
|
uint8_t mask = _pge_modKeysTable[args->a];
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
mask |= 8;
|
|
|
|
} else {
|
|
|
|
mask |= 4;
|
|
|
|
}
|
|
|
|
if (mask == _pge_inpKeysMask) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInpIdle(ObjectOpcodeArgs *args) {
|
|
|
|
if (_pge_inpKeysMask == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInpNoMod(ObjectOpcodeArgs *args) {
|
|
|
|
assert(args->a < 3);
|
|
|
|
uint8_t mask = _pge_modKeysTable[args->a];
|
|
|
|
if (((_pge_inpKeysMask & 0xF) | mask) == _pge_inpKeysMask) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_getCollision0u(ObjectOpcodeArgs *args) {
|
|
|
|
return col_getGridData(args->pge, 0, -args->a);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_getCollision00(ObjectOpcodeArgs *args) {
|
|
|
|
return col_getGridData(args->pge, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_getCollision0d(ObjectOpcodeArgs *args) {
|
|
|
|
return col_getGridData(args->pge, 0, args->a);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_getCollision1u(ObjectOpcodeArgs *args) {
|
|
|
|
return col_getGridData(args->pge, 1, -args->a);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_getCollision10(ObjectOpcodeArgs *args) {
|
|
|
|
return col_getGridData(args->pge, 1, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_getCollision1d(ObjectOpcodeArgs *args) {
|
|
|
|
return col_getGridData(args->pge, 1, args->a);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_getCollision2u(ObjectOpcodeArgs *args) {
|
|
|
|
return col_getGridData(args->pge, 2, -args->a);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_getCollision20(ObjectOpcodeArgs *args) {
|
|
|
|
return col_getGridData(args->pge, 2, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_getCollision2d(ObjectOpcodeArgs *args) {
|
|
|
|
return col_getGridData(args->pge, 2, args->a);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_doesNotCollide0u(ObjectOpcodeArgs *args) {
|
|
|
|
int16_t r = col_getGridData(args->pge, 0, -args->a);
|
|
|
|
if (r & 0xFFFF) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_doesNotCollide00(ObjectOpcodeArgs *args) {
|
|
|
|
int16_t r = col_getGridData(args->pge, 0, 0);
|
|
|
|
if (r & 0xFFFF) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_doesNotCollide0d(ObjectOpcodeArgs *args) {
|
|
|
|
int16_t r = col_getGridData(args->pge, 0, args->a);
|
|
|
|
if (r & 0xFFFF) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_doesNotCollide1u(ObjectOpcodeArgs *args) {
|
|
|
|
int16_t r = col_getGridData(args->pge, 1, -args->a);
|
|
|
|
if (r & 0xFFFF) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_doesNotCollide10(ObjectOpcodeArgs *args) {
|
|
|
|
int16_t r = col_getGridData(args->pge, 1, 0);
|
|
|
|
if (r & 0xFFFF) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_doesNotCollide1d(ObjectOpcodeArgs *args) {
|
|
|
|
int16_t r = col_getGridData(args->pge, 1, args->a);
|
|
|
|
if (r & 0xFFFF) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_doesNotCollide2u(ObjectOpcodeArgs *args) {
|
|
|
|
int16_t r = col_getGridData(args->pge, 2, -args->a);
|
|
|
|
if (r & 0xFFFF) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_doesNotCollide20(ObjectOpcodeArgs *args) {
|
|
|
|
int16_t r = col_getGridData(args->pge, 2, 0);
|
|
|
|
if (r & 0xFFFF) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_doesNotCollide2d(ObjectOpcodeArgs *args) {
|
|
|
|
int16_t r = col_getGridData(args->pge, 2, args->a);
|
|
|
|
if (r & 0xFFFF) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_collides0o0d(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 0, args->a) & 0xFFFF) {
|
|
|
|
if (col_getGridData(args->pge, 0, args->a + 1) == 0) {
|
|
|
|
if (col_getGridData(args->pge, -1, args->a) == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_collides2o2d(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 2, args->a) & 0xFFFF) {
|
|
|
|
if (col_getGridData(args->pge, 2, args->a + 1) == 0) {
|
|
|
|
if (col_getGridData(args->pge, 1, args->a) == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_collides0o0u(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 0, args->a) & 0xFFFF) {
|
|
|
|
if (col_getGridData(args->pge, 0, args->a - 1) == 0) {
|
|
|
|
if (col_getGridData(args->pge, -1, args->a) == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_collides2o2u(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 2, args->a) & 0xFFFF) {
|
|
|
|
if (col_getGridData(args->pge, 2, args->a - 1) == 0) {
|
|
|
|
if (col_getGridData(args->pge, 1, args->a) == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_collides2u2o(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 2, args->a - 1) & 0xFFFF) {
|
|
|
|
if (col_getGridData(args->pge, 2, args->a) == 0) {
|
|
|
|
if (col_getGridData(args->pge, 1, args->a - 1) == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInGroup(ObjectOpcodeArgs *args) {
|
|
|
|
GroupPGE *le = _pge_groupsTable[args->pge->index];
|
|
|
|
while (le) {
|
|
|
|
if (le->group_id == args->a) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
le = le->next_entry;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_updateGroup0(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
pge_updateGroup(pge->index, pge->init_PGE->counter_values[0], args->a);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_updateGroup1(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
pge_updateGroup(pge->index, pge->init_PGE->counter_values[1], args->a);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_updateGroup2(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
pge_updateGroup(pge->index, pge->init_PGE->counter_values[2], args->a);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_updateGroup3(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
pge_updateGroup(pge->index, pge->init_PGE->counter_values[3], args->a);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isPiegeDead(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
if (pge->life <= 0) {
|
|
|
|
if (pge->init_PGE->object_type == 10) {
|
|
|
|
_score += 100;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_collides1u2o(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 1, args->a - 1) & 0xFFFF) {
|
|
|
|
if (col_getGridData(args->pge, 2, args->a) == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_collides1u1o(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 1, args->a - 1) & 0xFFFF) {
|
|
|
|
if (col_getGridData(args->pge, 1, args->a) == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_collides1o1u(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 1, args->a - 1) == 0) {
|
|
|
|
if (col_getGridData(args->pge, 1, args->a) & 0xFFFF) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x2B(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_ZOrder(args->pge, args->a, &Game::pge_ZOrderIfTypeAndDifferentDirection, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x2C(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_ZOrder(args->pge, args->a, &Game::pge_ZOrderIfTypeAndSameDirection, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x2D(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_ZOrder(args->pge, args->a, &Game::pge_ZOrderByObj, 0) ^ 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_nop(ObjectOpcodeArgs *args) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_pickupObject(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = col_findPiege(args->pge, 3);
|
|
|
|
if (pge) {
|
|
|
|
pge_updateGroup(args->pge->index, pge->index, args->a);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_addItemToInventory(ObjectOpcodeArgs *args) {
|
|
|
|
pge_updateInventory(&_pgeLive[args->a], args->pge);
|
|
|
|
args->pge->room_location = 0xFF;
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_copyPiege(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *src = &_pgeLive[args->a];
|
|
|
|
LivePGE *dst = args->pge;
|
|
|
|
|
|
|
|
dst->pos_x = src->pos_x;
|
|
|
|
dst->pos_y = src->pos_y;
|
|
|
|
dst->room_location = src->room_location;
|
|
|
|
|
|
|
|
dst->flags &= 0xFE;
|
|
|
|
if (src->flags & 1) {
|
|
|
|
dst->flags |= 1;
|
|
|
|
}
|
|
|
|
pge_reorderInventory(args->pge);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_removeItemFromInventory(ObjectOpcodeArgs *args) {
|
|
|
|
if (args->pge->current_inventory_PGE != 0xFF) {
|
|
|
|
pge_updateGroup(args->pge->index, args->pge->current_inventory_PGE, args->a);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_canUseCurrentInventoryItem(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = &_pgeLive[0];
|
|
|
|
if (pge->current_inventory_PGE != 0xFF && _res._pgeInit[pge->current_inventory_PGE].object_id == args->a) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// useObject related
|
|
|
|
int Game::pge_o_unk0x34(ObjectOpcodeArgs *args) {
|
|
|
|
uint8_t mask = (_pge_inpKeysMask & 0xF) | _pge_modKeysTable[0];
|
|
|
|
if (mask == _pge_inpKeysMask) {
|
|
|
|
if (col_getGridData(args->pge, 2, -args->a) == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInpMod(ObjectOpcodeArgs *args) {
|
|
|
|
assert(args->a < 3);
|
|
|
|
uint8_t mask = _pge_modKeysTable[args->a];
|
|
|
|
if (mask == _pge_inpKeysMask) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_setCollisionState1(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_updateCollisionState(args->pge, args->a, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_setCollisionState0(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_updateCollisionState(args->pge, args->a, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInGroup1(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_isInGroup(args->pge, args->a, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInGroup2(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_isInGroup(args->pge, args->a, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInGroup3(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_isInGroup(args->pge, args->a, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInGroup4(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_isInGroup(args->pge, args->a, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x3C(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_ZOrder(args->pge, args->a, &Game::pge_ZOrderByAnimYIfType, args->b);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x3D(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_ZOrder(args->pge, args->a, &Game::pge_ZOrderByAnimY, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_setPiegeCounter(ObjectOpcodeArgs *args) {
|
|
|
|
args->pge->counter_value = args->a;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_decPiegeCounter(ObjectOpcodeArgs *args) {
|
|
|
|
args->pge->counter_value -= 1;
|
|
|
|
if (args->a == args->pge->counter_value) {
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x40(ObjectOpcodeArgs *args) {
|
|
|
|
int8_t pge_room = args->pge->room_location;
|
|
|
|
if (pge_room < 0 || pge_room >= 0x40) return 0;
|
|
|
|
int col_area;
|
|
|
|
if (_currentRoom == pge_room) {
|
|
|
|
col_area = 1;
|
|
|
|
} else if (_col_currentLeftRoom == pge_room) {
|
|
|
|
col_area = 0;
|
|
|
|
} else if (_col_currentRightRoom == pge_room) {
|
|
|
|
col_area = 2;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int16_t grid_pos_x = (args->pge->pos_x + 8) >> 4;
|
|
|
|
int16_t grid_pos_y = args->pge->pos_y / 72;
|
|
|
|
if (grid_pos_y >= 0 && grid_pos_y <= 2) {
|
|
|
|
grid_pos_y *= 16;
|
|
|
|
int16_t _cx = args->a;
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
_cx = -_cx;
|
|
|
|
}
|
|
|
|
int8_t _bl;
|
|
|
|
if (_cx >= 0) {
|
|
|
|
if (_cx > 0x10) {
|
|
|
|
_cx = 0x10;
|
|
|
|
}
|
|
|
|
int8_t *var2 = &_res._ctData[0x100] + pge_room * 0x70 + grid_pos_y * 2 + 0x10 + grid_pos_x;
|
|
|
|
uint8_t *var4 = _col_activeCollisionSlots + col_area * 0x30 + grid_pos_y + grid_pos_x;
|
|
|
|
int16_t var12 = grid_pos_x;
|
|
|
|
--_cx;
|
|
|
|
do {
|
|
|
|
--var12;
|
|
|
|
if (var12 < 0) {
|
|
|
|
--col_area;
|
|
|
|
if (col_area < 0) return 0;
|
|
|
|
pge_room = _res._ctData[CT_LEFT_ROOM + pge_room];
|
|
|
|
if (pge_room < 0) return 0;
|
|
|
|
var12 = 15;
|
|
|
|
var2 = &_res._ctData[0x101] + pge_room * 0x70 + grid_pos_y * 2 + 15 + 0x10;
|
|
|
|
var4 = var4 - 31;
|
|
|
|
}
|
|
|
|
--var4;
|
|
|
|
_bl = *var4;
|
|
|
|
if (_bl >= 0) {
|
|
|
|
CollisionSlot *col_slot = _col_slotsTable[_bl];
|
|
|
|
do {
|
|
|
|
if (args->pge != col_slot->live_pge && (col_slot->live_pge->flags & 4)) {
|
|
|
|
if (col_slot->live_pge->init_PGE->object_type == args->b) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
col_slot = col_slot->prev_slot;
|
|
|
|
} while (col_slot);
|
|
|
|
}
|
|
|
|
--var2;
|
|
|
|
if (*var2 != 0) return 0;
|
|
|
|
--_cx;
|
|
|
|
} while (_cx >= 0);
|
|
|
|
} else {
|
|
|
|
_cx = -_cx;
|
|
|
|
if (_cx > 0x10) {
|
|
|
|
_cx = 0x10;
|
|
|
|
}
|
|
|
|
int8_t *var2 = &_res._ctData[0x101] + pge_room * 0x70 + grid_pos_y * 2 + 0x10 + grid_pos_x;
|
|
|
|
uint8_t *var4 = _col_activeCollisionSlots + 1 + col_area * 0x30 + grid_pos_y + grid_pos_x;
|
|
|
|
int16_t var12 = grid_pos_x;
|
|
|
|
--_cx;
|
|
|
|
do {
|
|
|
|
++var12;
|
|
|
|
if (var12 == 0x10) {
|
|
|
|
++col_area;
|
|
|
|
if (col_area > 2) return 0;
|
|
|
|
pge_room = _res._ctData[CT_RIGHT_ROOM + pge_room];
|
|
|
|
if (pge_room < 0) return 0;
|
|
|
|
|
|
|
|
var12 = 0;
|
|
|
|
var2 = &_res._ctData[0x101] + pge_room * 0x70 + grid_pos_y * 2 + 0x10;
|
|
|
|
var4 += 32;
|
|
|
|
}
|
|
|
|
var4++;
|
|
|
|
_bl = *var4;
|
|
|
|
if (_bl >= 0) {
|
|
|
|
CollisionSlot *col_slot = _col_slotsTable[_bl];
|
|
|
|
do {
|
|
|
|
if (args->pge != col_slot->live_pge && (col_slot->live_pge->flags & 4)) {
|
|
|
|
if (col_slot->live_pge->init_PGE->object_type == args->b) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
col_slot = col_slot->prev_slot;
|
|
|
|
} while (col_slot);
|
|
|
|
}
|
|
|
|
_bl = *var2;
|
|
|
|
++var2;
|
|
|
|
if (_bl != 0) return 0;
|
|
|
|
--_cx;
|
|
|
|
} while (_cx >= 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_wakeUpPiege(ObjectOpcodeArgs *args) {
|
|
|
|
if (args->a <= 3) {
|
|
|
|
int16_t num = args->pge->init_PGE->counter_values[args->a];
|
|
|
|
if (num >= 0) {
|
|
|
|
LivePGE *pge = &_pgeLive[num];
|
|
|
|
pge->flags |= 4;
|
|
|
|
_pge_liveTable2[num] = pge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_removePiege(ObjectOpcodeArgs *args) {
|
|
|
|
if (args->a <= 3) {
|
|
|
|
int16_t num = args->pge->init_PGE->counter_values[args->a];
|
|
|
|
if (num >= 0) {
|
|
|
|
_pge_liveTable2[num] = 0;
|
|
|
|
_pgeLive[num].flags &= ~4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_removePiegeIfNotNear(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
if (!(pge->init_PGE->flags & 4)) goto kill_pge;
|
|
|
|
if (_currentRoom & 0x80) goto skip_pge;
|
|
|
|
if (pge->room_location & 0x80) goto kill_pge;
|
|
|
|
if (pge->room_location > 0x3F) goto kill_pge;
|
|
|
|
if (pge->room_location == _currentRoom) goto skip_pge;
|
|
|
|
if (pge->room_location == _res._ctData[CT_UP_ROOM + _currentRoom]) goto skip_pge;
|
|
|
|
if (pge->room_location == _res._ctData[CT_DOWN_ROOM + _currentRoom]) goto skip_pge;
|
|
|
|
if (pge->room_location == _res._ctData[CT_RIGHT_ROOM + _currentRoom]) goto skip_pge;
|
|
|
|
if (pge->room_location == _res._ctData[CT_LEFT_ROOM + _currentRoom]) goto skip_pge;
|
|
|
|
|
|
|
|
kill_pge:
|
|
|
|
pge->flags &= ~4;
|
|
|
|
pge->collision_slot = 0xFF;
|
|
|
|
_pge_liveTable2[pge->index] = 0;
|
|
|
|
|
|
|
|
skip_pge:
|
|
|
|
_pge_playAnimSound = false;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_loadPiegeCounter(ObjectOpcodeArgs *args) {
|
|
|
|
args->pge->counter_value = args->pge->init_PGE->counter_values[args->a];
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x45(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_ZOrder(args->pge, args->a, &Game::pge_ZOrderByNumber, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x46(ObjectOpcodeArgs *args) {
|
|
|
|
_pge_compareVar1 = 0;
|
|
|
|
pge_ZOrder(args->pge, args->a, &Game::pge_ZOrderIfDifferentDirection, 0);
|
|
|
|
return _pge_compareVar1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x47(ObjectOpcodeArgs *args) {
|
|
|
|
_pge_compareVar2 = 0;
|
|
|
|
pge_ZOrder(args->pge, args->a, &Game::pge_ZOrderIfSameDirection, 0);
|
|
|
|
return _pge_compareVar2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// used with Ian in level2
|
|
|
|
int Game::pge_o_unk0x48(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = col_findPiege(&_pgeLive[0], args->pge->init_PGE->counter_values[0]);
|
|
|
|
if (pge && pge->life == args->pge->life) {
|
|
|
|
pge_updateGroup(args->pge->index, pge->index, args->a);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x49(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_ZOrder(&_pgeLive[0], args->a, &Game::pge_ZOrderIfIndex, args->pge->init_PGE->counter_values[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x4A(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
pge->room_location = 0xFE;
|
|
|
|
pge->flags &= ~4;
|
|
|
|
_pge_liveTable2[pge->index] = 0;
|
|
|
|
LivePGE *inv_pge = pge_getInventoryItemBefore(&_pgeLive[args->a], pge);
|
|
|
|
if (inv_pge == &_pgeLive[args->a]) {
|
|
|
|
if (pge->index != inv_pge->current_inventory_PGE) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (pge->index != inv_pge->next_inventory_PGE) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pge_removeFromInventory(inv_pge, pge, &_pgeLive[args->a]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_killPiege(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
pge->room_location = 0xFE;
|
|
|
|
pge->flags &= ~4;
|
|
|
|
_pge_liveTable2[pge->index] = 0;
|
|
|
|
if (pge->init_PGE->object_type == 10) {
|
|
|
|
_score += 200;
|
|
|
|
}
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInCurrentRoom(ObjectOpcodeArgs *args) {
|
|
|
|
return (args->pge->room_location == _currentRoom) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isNotInCurrentRoom(ObjectOpcodeArgs *args) {
|
|
|
|
return (args->pge->room_location == _currentRoom) ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_scrollPosY(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
args->pge->pos_y += args->a;
|
|
|
|
uint8_t pge_num = pge->current_inventory_PGE;
|
|
|
|
while (pge_num != 0xFF) {
|
|
|
|
pge = &_pgeLive[pge_num];
|
|
|
|
pge->pos_y += args->a;
|
|
|
|
pge_num = pge->next_inventory_PGE;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_playDefaultDeathCutscene(ObjectOpcodeArgs *args) {
|
|
|
|
if (_deathCutsceneCounter == 0) {
|
|
|
|
_deathCutsceneCounter = args->a;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x50(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_ZOrder(args->pge, args->a, &Game::pge_ZOrderByObj, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x52(ObjectOpcodeArgs *args) {
|
|
|
|
return col_detectHit(args->pge, args->a, args->b, &Game::col_detectHitCallback4, &Game::col_detectHitCallback1, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x53(ObjectOpcodeArgs *args) {
|
|
|
|
return col_detectHit(args->pge, args->a, args->b, &Game::col_detectHitCallback5, &Game::col_detectHitCallback1, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isPiegeNear(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_findPiege(&_pgeLive[0], args->a) != 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_setLife(ObjectOpcodeArgs *args) {
|
|
|
|
args->pge->life = args->a;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_incLife(ObjectOpcodeArgs *args) {
|
|
|
|
args->pge->life += args->a;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// level2, Ian
|
|
|
|
int Game::pge_op_setPiegeDefaultAnim(ObjectOpcodeArgs *args) {
|
|
|
|
assert(args->a >= 0 && args->a < 4);
|
|
|
|
int16_t r = args->pge->init_PGE->counter_values[args->a];
|
|
|
|
args->pge->room_location = r;
|
|
|
|
if (r == 1) {
|
2016-03-20 17:00:00 +01:00
|
|
|
// this happens after death tower, on earth, when Conrad passes
|
|
|
|
// by the first policeman who's about to shoot him in the back
|
2015-08-02 18:00:00 +02:00
|
|
|
_loadMap = true;
|
|
|
|
}
|
|
|
|
pge_setupDefaultAnim(args->pge);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_setLifeCounter(ObjectOpcodeArgs *args) {
|
|
|
|
_pgeLive[args->a].life = args->pge->init_PGE->counter_values[0];
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_decLifeCounter(ObjectOpcodeArgs *args) {
|
|
|
|
args->pge->life = _pgeLive[args->a].life - 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_playCutscene(ObjectOpcodeArgs *args) {
|
|
|
|
if (_deathCutsceneCounter == 0) {
|
|
|
|
_cut._id = args->a;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isTempVar2Set(ObjectOpcodeArgs *args) {
|
|
|
|
if (_pge_opTempVar2 == args->a) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_playDeathCutscene(ObjectOpcodeArgs *args) {
|
|
|
|
if (_deathCutsceneCounter == 0) {
|
|
|
|
_deathCutsceneCounter = args->pge->init_PGE->counter_values[3] + 1;
|
|
|
|
_cut._deathCutsceneId = args->a;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x5D(ObjectOpcodeArgs *args) {
|
|
|
|
return col_detectHit(args->pge, args->a, args->b, &Game::col_detectHitCallback4, &Game::col_detectHitCallback6, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x5E(ObjectOpcodeArgs *args) {
|
|
|
|
return col_detectHit(args->pge, args->a, args->b, &Game::col_detectHitCallback5, &Game::col_detectHitCallback6, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x5F(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
|
|
|
|
int8_t pge_room = pge->room_location;
|
|
|
|
if (pge_room < 0 || pge_room >= 0x40) return 0;
|
|
|
|
|
|
|
|
int16_t dx;
|
|
|
|
int16_t _cx = pge->init_PGE->counter_values[0];
|
|
|
|
if (_cx <= 0) {
|
|
|
|
dx = 1;
|
|
|
|
_cx = -_cx;
|
|
|
|
} else {
|
|
|
|
dx = -1;
|
|
|
|
}
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
dx = -dx;
|
|
|
|
}
|
|
|
|
int16_t grid_pos_x = (pge->pos_x + 8) >> 4;
|
|
|
|
int16_t grid_pos_y = 0;
|
|
|
|
do {
|
|
|
|
int16_t _ax = col_getGridData(pge, 1, -grid_pos_y);
|
|
|
|
if (_ax != 0) {
|
|
|
|
if (!(_ax & 2) || args->a != 1) {
|
|
|
|
pge->room_location = pge_room;
|
|
|
|
pge->pos_x = grid_pos_x * 16;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (grid_pos_x < 0) {
|
|
|
|
pge_room = _res._ctData[CT_LEFT_ROOM + pge_room];
|
|
|
|
if (pge_room < 0 || pge_room >= 0x40) return 0;
|
|
|
|
grid_pos_x += 16;
|
|
|
|
} else if (grid_pos_x > 15) {
|
|
|
|
pge_room = _res._ctData[CT_RIGHT_ROOM + pge_room];
|
|
|
|
if (pge_room < 0 || pge_room >= 0x40) return 0;
|
|
|
|
grid_pos_x -= 16;
|
|
|
|
}
|
|
|
|
grid_pos_x += dx;
|
|
|
|
++grid_pos_y;
|
|
|
|
} while (grid_pos_y <= _cx);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_findAndCopyPiege(ObjectOpcodeArgs *args) {
|
|
|
|
GroupPGE *le = _pge_groupsTable[args->pge->index];
|
|
|
|
while (le) {
|
|
|
|
if (le->group_id == args->a) {
|
|
|
|
args->a = le->index;
|
|
|
|
args->b = 0;
|
|
|
|
pge_op_copyPiege(args);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
le = le->next_entry;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInRandomRange(ObjectOpcodeArgs *args) {
|
|
|
|
uint16_t n = args->a;
|
|
|
|
if (n != 0) {
|
|
|
|
if ((getRandomNumber() % n) == 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x62(ObjectOpcodeArgs *args) {
|
|
|
|
return col_detectHit(args->pge, args->a, args->b, &Game::col_detectHitCallback3, &Game::col_detectHitCallback1, 0, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x63(ObjectOpcodeArgs *args) {
|
|
|
|
return col_detectHit(args->pge, args->a, args->b, &Game::col_detectHitCallback2, &Game::col_detectHitCallback1, 0, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x64(ObjectOpcodeArgs *args) {
|
|
|
|
return col_detectGunHit(args->pge, args->a, args->b, &Game::col_detectGunHitCallback3, &Game::col_detectGunHitCallback1, 1, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_addToCredits(ObjectOpcodeArgs *args) {
|
|
|
|
assert(args->a >= 0 && args->a < 3);
|
|
|
|
uint8_t pge = args->pge->init_PGE->counter_values[args->a];
|
|
|
|
int16_t val = args->pge->init_PGE->counter_values[args->a + 1];
|
|
|
|
_pgeLive[pge].life += val;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_subFromCredits(ObjectOpcodeArgs *args) {
|
|
|
|
assert(args->a >= 0 && args->a < 3);
|
|
|
|
uint8_t pge = args->pge->init_PGE->counter_values[args->a];
|
|
|
|
int16_t val = args->pge->init_PGE->counter_values[args->a + 1];
|
|
|
|
_pgeLive[pge].life -= val;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x67(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 1, -args->a) & 2) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_setCollisionState2(ObjectOpcodeArgs *args) {
|
|
|
|
return pge_updateCollisionState(args->pge, args->a, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_saveState(ObjectOpcodeArgs *args) {
|
|
|
|
_saveStateCompleted = true;
|
|
|
|
_validSaveState = saveGameState(0);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
// useGun related
|
|
|
|
int Game::pge_o_unk0x6A(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *_si = args->pge;
|
|
|
|
int8_t pge_room = _si->room_location;
|
|
|
|
if (pge_room < 0 || pge_room >= 0x40) return 0;
|
|
|
|
int8_t _bl;
|
|
|
|
int col_area = 0;
|
|
|
|
int8_t *ct_data;
|
|
|
|
if (_currentRoom == pge_room) {
|
|
|
|
col_area = 1;
|
|
|
|
} else if (_col_currentLeftRoom == pge_room) {
|
|
|
|
col_area = 0;
|
|
|
|
} else if (_col_currentRightRoom == pge_room) {
|
|
|
|
col_area = 2;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int16_t grid_pos_x = (_si->pos_x + 8) >> 4;
|
|
|
|
int16_t grid_pos_y = (_si->pos_y / 72);
|
|
|
|
if (grid_pos_y >= 0 && grid_pos_y <= 2) {
|
|
|
|
grid_pos_y *= 16;
|
|
|
|
int16_t _cx = args->a;
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
_cx = -_cx;
|
|
|
|
}
|
|
|
|
if (_cx >= 0) {
|
|
|
|
if (_cx > 0x10) {
|
|
|
|
_cx = 0x10;
|
|
|
|
}
|
|
|
|
ct_data = &_res._ctData[0x100] + pge_room * 0x70 + grid_pos_y * 2 + 0x10 + grid_pos_x;
|
|
|
|
uint8_t *var4 = _col_activeCollisionSlots + col_area * 0x30 + grid_pos_y + grid_pos_x;
|
|
|
|
++var4;
|
|
|
|
++ct_data;
|
|
|
|
int16_t varA = grid_pos_x;
|
|
|
|
do {
|
|
|
|
--varA;
|
|
|
|
if (varA < 0) {
|
|
|
|
--col_area;
|
|
|
|
if (col_area < 0) return 0;
|
|
|
|
pge_room = _res._ctData[CT_LEFT_ROOM + pge_room];
|
|
|
|
if (pge_room < 0) return 0;
|
|
|
|
varA = 0xF;
|
|
|
|
ct_data = &_res._ctData[0x101] + pge_room * 0x70 + grid_pos_y * 2 + 0x10 + varA;
|
|
|
|
var4 -= 0x1F;
|
|
|
|
}
|
|
|
|
--var4;
|
|
|
|
_bl = *var4;
|
|
|
|
if (_bl >= 0) {
|
|
|
|
CollisionSlot *collision_slot = _col_slotsTable[_bl];
|
|
|
|
do {
|
|
|
|
_si = collision_slot->live_pge;
|
|
|
|
if (args->pge != _si && (_si->flags & 4) && _si->life >= 0) {
|
|
|
|
if (_si->init_PGE->object_type == 1 || _si->init_PGE->object_type == 10) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
collision_slot = collision_slot->prev_slot;
|
|
|
|
} while (collision_slot);
|
|
|
|
}
|
|
|
|
--ct_data;
|
|
|
|
if (*ct_data != 0) return 0;
|
|
|
|
--_cx;
|
|
|
|
} while (_cx >= 0);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
_cx = -_cx;
|
|
|
|
if (_cx > 0x10) {
|
|
|
|
_cx = 0x10;
|
|
|
|
}
|
|
|
|
ct_data = &_res._ctData[0x101] + pge_room * 0x70 + grid_pos_y * 2 + 0x10 + grid_pos_x;
|
|
|
|
uint8_t *var4 = _col_activeCollisionSlots + 1 + col_area * 0x30 + grid_pos_y + grid_pos_x;
|
|
|
|
int16_t varA = grid_pos_x;
|
|
|
|
goto loc_0_15446;
|
|
|
|
do {
|
|
|
|
++varA;
|
|
|
|
if (varA == 0x10) {
|
|
|
|
++col_area;
|
|
|
|
if (col_area > 2) return 0;
|
|
|
|
pge_room = _res._ctData[CT_RIGHT_ROOM + pge_room];
|
|
|
|
if (pge_room < 0) return 0;
|
|
|
|
varA = 0;
|
|
|
|
ct_data = &_res._ctData[0x100] + pge_room * 0x70 + grid_pos_y * 2 + 0x10 + varA;
|
|
|
|
var4 += 0x20;
|
|
|
|
}
|
|
|
|
loc_0_15446:
|
|
|
|
_bl = *var4;
|
|
|
|
++var4;
|
|
|
|
if (_bl >= 0) {
|
|
|
|
CollisionSlot *collision_slot = _col_slotsTable[_bl];
|
|
|
|
do {
|
|
|
|
_si = collision_slot->live_pge;
|
|
|
|
if (args->pge != _si && (_si->flags & 4) && _si->life >= 0) {
|
|
|
|
if (_si->init_PGE->object_type == 1 || _si->init_PGE->object_type == 10) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
collision_slot = collision_slot->prev_slot;
|
|
|
|
} while (collision_slot);
|
|
|
|
}
|
|
|
|
_bl = *ct_data;
|
|
|
|
++ct_data;
|
|
|
|
if (_bl != 0) return 0;
|
|
|
|
--_cx;
|
|
|
|
} while (_cx >= 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isInGroupSlice(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
GroupPGE *le = _pge_groupsTable[pge->index];
|
|
|
|
if (le) {
|
|
|
|
if (args->a == 0) {
|
|
|
|
do {
|
|
|
|
if (le->group_id == 1 || le->group_id == 2) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
le = le->next_entry;
|
|
|
|
} while (le);
|
|
|
|
} else {
|
|
|
|
do {
|
|
|
|
if (le->group_id == 3 || le->group_id == 4) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
le = le->next_entry;
|
|
|
|
} while (le);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x6C(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = col_findPiege(&_pgeLive[0], args->pge->init_PGE->counter_values[0]);
|
|
|
|
if (pge) {
|
|
|
|
if (pge->life <= args->pge->life) {
|
|
|
|
pge_updateGroup(args->pge->index, pge->index, args->a);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isCollidingObject(ObjectOpcodeArgs *args) {
|
|
|
|
uint8_t r = col_findCurrentCollidingObject(args->pge, 3, 0xFF, 0xFF, 0);
|
|
|
|
if (r == args->a) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// elevator
|
|
|
|
int Game::pge_o_unk0x6E(ObjectOpcodeArgs *args) {
|
|
|
|
GroupPGE *le = _pge_groupsTable[args->pge->index];
|
|
|
|
while (le) {
|
|
|
|
if (args->a == le->group_id) {
|
|
|
|
pge_updateInventory(&_pgeLive[le->index], args->pge);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
le = le->next_entry;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x6F(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
GroupPGE *le = _pge_groupsTable[pge->index];
|
|
|
|
while (le) {
|
|
|
|
if (args->a == le->group_id) {
|
|
|
|
pge_updateGroup(pge->index, le->index, 0xC);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
le = le->next_entry;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x70(ObjectOpcodeArgs *args) {
|
|
|
|
uint8_t pge_num = args->pge->current_inventory_PGE;
|
|
|
|
while (pge_num != 0xFF) {
|
|
|
|
pge_updateGroup(args->pge->index, _pgeLive[pge_num].index, args->a);
|
|
|
|
pge_num = _pgeLive[pge_num].next_inventory_PGE;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// elevator
|
|
|
|
int Game::pge_o_unk0x71(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
GroupPGE *le = _pge_groupsTable[pge->index];
|
|
|
|
while (le) {
|
|
|
|
if (le->group_id == args->a) {
|
|
|
|
pge_reorderInventory(args->pge);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
le = le->next_entry;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x72(ObjectOpcodeArgs *args) {
|
|
|
|
int8_t *var4 = &_res._ctData[0x100] + args->pge->room_location * 0x70;
|
|
|
|
var4 += (((args->pge->pos_y / 36) & ~1) + args->a) * 16 + (args->pge->pos_x + 8) / 16;
|
|
|
|
|
|
|
|
CollisionSlot2 *_di = _col_slots2Next;
|
|
|
|
int _cx = 0x100;
|
|
|
|
while (_di && _cx != 0) {
|
|
|
|
if (_di->unk2 != var4) {
|
|
|
|
_di = _di->next_slot;
|
|
|
|
--_cx;
|
|
|
|
} else {
|
|
|
|
memcpy(_di->unk2, _di->data_buf, _di->data_size + 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0xFFFF; // XXX var4;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x73(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = col_findPiege(args->pge, args->a);
|
|
|
|
if (pge != 0) {
|
|
|
|
pge_updateInventory(pge, args->pge);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_collides4u(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 4, -args->a) != 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_doesNotCollide4u(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 4, -args->a) == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isBelowConrad(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *_si = args->pge;
|
|
|
|
LivePGE *pge_conrad = &_pgeLive[0];
|
|
|
|
if (pge_conrad->room_location == _si->room_location) {
|
|
|
|
if ((pge_conrad->pos_y - 8) / 72 < _si->pos_y / 72) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
} else if (!(_si->room_location & 0x80) && _si->room_location < 0x40) {
|
|
|
|
if (pge_conrad->room_location == _res._ctData[CT_UP_ROOM + _si->room_location]) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isAboveConrad(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *_si = args->pge;
|
|
|
|
LivePGE *pge_conrad = &_pgeLive[0];
|
|
|
|
if (pge_conrad->room_location == _si->room_location) {
|
|
|
|
if ((pge_conrad->pos_y - 8) / 72 > _si->pos_y / 72) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
} else if (!(_si->room_location & 0x80) && _si->room_location < 0x40) {
|
|
|
|
if (pge_conrad->room_location == _res._ctData[CT_DOWN_ROOM + _si->room_location]) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isNotFacingConrad(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
LivePGE *pge_conrad = &_pgeLive[0];
|
|
|
|
if (pge->pos_y / 72 == (pge_conrad->pos_y - 8) / 72) { // same grid cell
|
|
|
|
if (pge->room_location == pge_conrad->room_location) {
|
|
|
|
if (args->a == 0) {
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
if (pge->pos_x < pge_conrad->pos_x) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (pge->pos_x > pge_conrad->pos_x) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int16_t dx;
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
dx = pge_conrad->pos_x - pge->pos_x;
|
|
|
|
} else {
|
|
|
|
dx = pge->pos_x - pge_conrad->pos_x;
|
|
|
|
}
|
|
|
|
if (dx > 0 && dx < args->a * 16) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (args->a == 0) {
|
|
|
|
if (!(pge->room_location & 0x80) && pge->room_location < 0x40) {
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
if (pge_conrad->room_location == _res._ctData[CT_RIGHT_ROOM + pge->room_location])
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
if (pge_conrad->room_location == _res._ctData[CT_LEFT_ROOM + pge->room_location])
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isFacingConrad(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
LivePGE *pge_conrad = &_pgeLive[0];
|
|
|
|
if (pge->pos_y / 72 == (pge_conrad->pos_y - 8) / 72) {
|
|
|
|
if (pge->room_location == pge_conrad->room_location) {
|
|
|
|
if (args->a == 0) {
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
if (pge->pos_x > pge_conrad->pos_x) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (pge->pos_x <= pge_conrad->pos_x) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int16_t dx;
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
dx = pge->pos_x - pge_conrad->pos_x;
|
|
|
|
} else {
|
|
|
|
dx = pge_conrad->pos_x - pge->pos_x;
|
|
|
|
}
|
|
|
|
if (dx > 0 && dx < args->a * 16) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (args->a == 0) {
|
|
|
|
if (!(pge->room_location & 0x80) && pge->room_location < 0x40) {
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
if (pge_conrad->room_location == _res._ctData[CT_LEFT_ROOM + pge->room_location])
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
if (pge_conrad->room_location == _res._ctData[CT_RIGHT_ROOM + pge->room_location])
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_collides2u1u(ObjectOpcodeArgs *args) {
|
|
|
|
if (col_getGridData(args->pge, 1, -args->a) == 0) {
|
|
|
|
if (col_getGridData(args->pge, 2, -(args->a + 1)) & 0xFFFF) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_displayText(ObjectOpcodeArgs *args) {
|
|
|
|
_textToDisplay = args->a;
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x7C(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = col_findPiege(args->pge, 3);
|
|
|
|
if (pge == 0) {
|
|
|
|
pge = col_findPiege(args->pge, 5);
|
|
|
|
if (pge == 0) {
|
|
|
|
pge = col_findPiege(args->pge, 9);
|
|
|
|
if (pge == 0) {
|
|
|
|
pge = col_findPiege(args->pge, 0xFFFF);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pge != 0) {
|
|
|
|
pge_updateGroup(args->pge->index, pge->index, args->a);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_playSound(ObjectOpcodeArgs *args) {
|
|
|
|
uint8_t sfxId = args->a & 0xFF;
|
|
|
|
uint8_t softVol = args->a >> 8;
|
|
|
|
playSound(sfxId, softVol);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x7E(ObjectOpcodeArgs *args) {
|
|
|
|
_pge_compareVar1 = 0;
|
|
|
|
pge_ZOrder(args->pge, args->a, &Game::pge_ZOrderByIndex, 0);
|
|
|
|
return _pge_compareVar1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x7F(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *_si = args->pge;
|
|
|
|
uint8_t var4 = _si->collision_slot;
|
|
|
|
uint8_t var2 = _si->index;
|
|
|
|
while (var4 != 0xFF) {
|
|
|
|
CollisionSlot *slot = _col_slotsTable[var4];
|
|
|
|
while (slot) {
|
|
|
|
if (slot->live_pge != args->pge) {
|
|
|
|
if (slot->live_pge->init_PGE->object_type == 3 && var2 != slot->live_pge->unkF) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (slot->live_pge == args->pge) {
|
|
|
|
var4 = slot->index;
|
|
|
|
}
|
|
|
|
slot = slot->prev_slot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_setPiegePosX(ObjectOpcodeArgs *args) {
|
|
|
|
uint8_t pge_num = args->pge->unkF;
|
|
|
|
if (pge_num != 0xFF) {
|
|
|
|
args->pge->pos_x = _pgeLive[pge_num].pos_x;
|
|
|
|
}
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_setPiegePosModX(ObjectOpcodeArgs *args) {
|
|
|
|
uint8_t pge_num = args->pge->unkF;
|
|
|
|
if (pge_num != 0xFF) {
|
|
|
|
int16_t dx = _pgeLive[pge_num].pos_x % 256;
|
|
|
|
if (dx >= args->pge->pos_x) {
|
|
|
|
dx -= args->pge->pos_x;
|
|
|
|
}
|
|
|
|
args->pge->pos_x += dx;
|
|
|
|
}
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
// taxi, level4
|
|
|
|
int Game::pge_op_changeRoom(ObjectOpcodeArgs *args) {
|
|
|
|
InitPGE *init_pge_1 = args->pge->init_PGE;
|
|
|
|
assert(args->a >= 0 && args->a < 3);
|
|
|
|
int16_t _ax = init_pge_1->counter_values[args->a];
|
|
|
|
int16_t _bx = init_pge_1->counter_values[args->a + 1];
|
|
|
|
LivePGE *live_pge_1 = &_pgeLive[_bx];
|
|
|
|
LivePGE *live_pge_2 = &_pgeLive[_ax];
|
|
|
|
int8_t pge_room = live_pge_1->room_location;
|
|
|
|
if (pge_room >= 0 && pge_room < 0x40) {
|
|
|
|
int8_t _al = live_pge_2->room_location;
|
|
|
|
live_pge_2->pos_x = live_pge_1->pos_x;
|
|
|
|
live_pge_2->pos_y = live_pge_1->pos_y;
|
|
|
|
live_pge_2->room_location = live_pge_1->room_location;
|
|
|
|
pge_addToCurrentRoomList(live_pge_2, _al);
|
|
|
|
InitPGE *init_pge_2 = live_pge_2->init_PGE;
|
|
|
|
init_pge_1 = live_pge_1->init_PGE;
|
|
|
|
if (init_pge_2->obj_node_number == init_pge_1->obj_node_number) {
|
|
|
|
live_pge_2->flags &= 0xFE;
|
|
|
|
if (live_pge_1->flags & 1) {
|
|
|
|
live_pge_2->flags |= 1;
|
|
|
|
}
|
|
|
|
live_pge_2->obj_type = live_pge_1->obj_type;
|
|
|
|
live_pge_2->anim_seq = 0;
|
|
|
|
assert(init_pge_2->obj_node_number < _res._numObjectNodes);
|
|
|
|
ObjectNode *on = _res._objectNodesMap[init_pge_2->obj_node_number];
|
|
|
|
Object *obj = on->objects;
|
|
|
|
int i = 0;
|
|
|
|
while (obj->type != live_pge_2->obj_type) {
|
|
|
|
++i;
|
|
|
|
++obj;
|
|
|
|
}
|
|
|
|
live_pge_2->first_obj_number = i;
|
|
|
|
}
|
|
|
|
if (init_pge_2->object_type == 1) {
|
|
|
|
if (_currentRoom != live_pge_2->room_location) {
|
|
|
|
_currentRoom = live_pge_2->room_location;
|
|
|
|
loadLevelMap();
|
|
|
|
_vid.fullRefresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pge_setupDefaultAnim(live_pge_2);
|
|
|
|
}
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
// called for example before using gun, to check its presence
|
|
|
|
int Game::pge_op_hasInventoryItem(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = &_pgeLive[0];
|
|
|
|
uint8_t _dl = pge->current_inventory_PGE;
|
|
|
|
while (_dl != 0xFF) {
|
|
|
|
pge = &_pgeLive[_dl];
|
|
|
|
if (pge->init_PGE->object_id == args->a) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
_dl = pge->next_inventory_PGE;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_changeLevel(ObjectOpcodeArgs *args) {
|
|
|
|
_currentLevel = args->a - 1;
|
|
|
|
return _currentLevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_shakeScreen(ObjectOpcodeArgs *args) {
|
|
|
|
_vid._shakeOffset = getRandomNumber() & 7;
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_o_unk0x86(ObjectOpcodeArgs *args) {
|
|
|
|
return col_detectGunHit(args->pge, args->a, args->b, &Game::col_detectGunHitCallback2, &Game::col_detectGunHitCallback1, 1, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_playSoundGroup(ObjectOpcodeArgs *args) {
|
|
|
|
assert(args->a < 4);
|
|
|
|
uint16_t c = args->pge->init_PGE->counter_values[args->a];
|
|
|
|
uint8_t sfxId = c & 0xFF;
|
|
|
|
uint8_t softVol = c >> 8;
|
|
|
|
playSound(sfxId, softVol);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_adjustPos(ObjectOpcodeArgs *args) {
|
|
|
|
LivePGE *pge = args->pge;
|
|
|
|
pge->pos_x &= 0xFFF0;
|
|
|
|
if (pge->pos_y != 70 && pge->pos_y != 142 && pge->pos_y != 214) {
|
|
|
|
pge->pos_y = ((pge->pos_y / 72) + 1) * 72 - 2;
|
|
|
|
}
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_setTempVar1(ObjectOpcodeArgs *args) {
|
|
|
|
_pge_opTempVar1 = args->a;
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_op_isTempVar1Set(ObjectOpcodeArgs *args) {
|
|
|
|
if (_pge_opTempVar1 != args->a) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_setCurrentInventoryObject(LivePGE *pge) {
|
|
|
|
LivePGE *_bx = pge_getInventoryItemBefore(&_pgeLive[0], pge);
|
|
|
|
if (_bx == &_pgeLive[0]) {
|
|
|
|
if (_bx->current_inventory_PGE != pge->index) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (_bx->next_inventory_PGE != pge->index) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pge_removeFromInventory(_bx, pge, &_pgeLive[0]);
|
|
|
|
pge_addToInventory(&_pgeLive[0], pge, &_pgeLive[0]);
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_updateInventory(LivePGE *pge1, LivePGE *pge2) {
|
|
|
|
if (pge2->unkF != 0xFF) {
|
|
|
|
pge_reorderInventory(pge2);
|
|
|
|
}
|
|
|
|
LivePGE *_ax = pge_getInventoryItemBefore(pge1, 0);
|
|
|
|
pge_addToInventory(_ax, pge2, pge1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_reorderInventory(LivePGE *pge) {
|
|
|
|
if (pge->unkF != 0xFF) {
|
|
|
|
LivePGE *_bx = &_pgeLive[pge->unkF];
|
|
|
|
LivePGE *_di = pge_getInventoryItemBefore(_bx, pge);
|
|
|
|
if (_di == _bx) {
|
|
|
|
if (_di->current_inventory_PGE == pge->index) {
|
|
|
|
pge_removeFromInventory(_di, pge, _bx);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (_di->next_inventory_PGE == pge->index) {
|
|
|
|
pge_removeFromInventory(_di, pge, _bx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LivePGE *Game::pge_getInventoryItemBefore(LivePGE *pge, LivePGE *last_pge) {
|
|
|
|
LivePGE *_di = pge;
|
|
|
|
uint8_t n = _di->current_inventory_PGE;
|
|
|
|
while (n != 0xFF) {
|
|
|
|
LivePGE *_si = &_pgeLive[n];
|
|
|
|
if (_si == last_pge) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
_di = _si;
|
|
|
|
n = _di->next_inventory_PGE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return _di;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_addToInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3) {
|
|
|
|
pge2->unkF = pge3->index;
|
|
|
|
if (pge1 == pge3) {
|
|
|
|
pge2->next_inventory_PGE = pge1->current_inventory_PGE;
|
|
|
|
pge1->current_inventory_PGE = pge2->index;
|
|
|
|
} else {
|
|
|
|
pge2->next_inventory_PGE = pge1->next_inventory_PGE;
|
|
|
|
pge1->next_inventory_PGE = pge2->index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t var8) {
|
|
|
|
uint8_t pge_unk1C = pge->init_PGE->unk1C;
|
|
|
|
if (!(pge->room_location & 0x80) && pge->room_location < 0x40) {
|
|
|
|
int8_t *grid_data = &_res._ctData[0x100] + 0x70 * pge->room_location;
|
|
|
|
int16_t pge_pos_y = ((pge->pos_y / 36) & ~1) + pge_dy;
|
|
|
|
int16_t pge_pos_x = (pge->pos_x + 8) >> 4;
|
|
|
|
|
|
|
|
grid_data += pge_pos_x + pge_pos_y * 16;
|
|
|
|
|
|
|
|
CollisionSlot2 *slot1 = _col_slots2Next;
|
|
|
|
int16_t i = 255;
|
|
|
|
pge_pos_x = i;
|
|
|
|
if (_pge_currentPiegeFacingDir) {
|
|
|
|
i = pge_unk1C - 1;
|
|
|
|
grid_data -= i;
|
|
|
|
}
|
|
|
|
while (slot1) {
|
|
|
|
if (slot1->unk2 == grid_data) {
|
|
|
|
slot1->data_size = pge_unk1C - 1;
|
|
|
|
assert(pge_unk1C < 0x70);
|
|
|
|
memset(grid_data, var8, pge_unk1C);
|
|
|
|
grid_data += pge_unk1C;
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
++i;
|
|
|
|
slot1 = slot1->next_slot;
|
|
|
|
if (--i == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_col_slots2Cur < &_col_slots2[255]) {
|
|
|
|
slot1 = _col_slots2Cur;
|
|
|
|
slot1->unk2 = grid_data;
|
|
|
|
slot1->data_size = pge_unk1C - 1;
|
|
|
|
uint8_t *dst = &slot1->data_buf[0];
|
|
|
|
int8_t *src = grid_data;
|
|
|
|
int n = pge_unk1C;
|
|
|
|
assert(n < 0x10);
|
|
|
|
while (n--) {
|
|
|
|
*dst++ = *src;
|
|
|
|
*src++ = var8;
|
|
|
|
}
|
|
|
|
++_col_slots2Cur;
|
|
|
|
slot1->next_slot = _col_slots2Next;
|
|
|
|
_col_slots2Next = slot1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_ZOrder(LivePGE *pge, int16_t num, pge_ZOrderCallback compare, uint16_t unk) {
|
|
|
|
uint8_t slot = pge->collision_slot;
|
|
|
|
while (slot != 0xFF) {
|
|
|
|
CollisionSlot *cs = _col_slotsTable[slot];
|
|
|
|
if (cs == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
uint8_t slot_bak = slot;
|
|
|
|
slot = 0xFF;
|
|
|
|
while (cs != 0) {
|
|
|
|
if ((this->*compare)(cs->live_pge, pge, num, unk) != 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (pge == cs->live_pge) {
|
|
|
|
slot = cs->index;
|
|
|
|
}
|
|
|
|
cs = cs->prev_slot;
|
|
|
|
if (slot == slot_bak) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_updateGroup(uint8_t idx, uint8_t unk1, int16_t unk2) {
|
|
|
|
debug(DBG_GAME, "Game::pge_updateGroup() idx=0x%X unk1=0x%X unk2=0x%X", idx, unk1, unk2);
|
|
|
|
LivePGE *pge = &_pgeLive[unk1];
|
|
|
|
if (!(pge->flags & 4)) {
|
|
|
|
if (!(pge->init_PGE->flags & 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pge->flags |= 4;
|
|
|
|
_pge_liveTable2[unk1] = pge;
|
|
|
|
}
|
|
|
|
if (unk2 <= 4) {
|
|
|
|
uint8_t pge_room = pge->room_location;
|
|
|
|
pge = &_pgeLive[idx];
|
|
|
|
if (pge_room != pge->room_location) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (unk1 == 0 && _blinkingConradCounter != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// XXX
|
|
|
|
}
|
|
|
|
GroupPGE *le = _pge_nextFreeGroup;
|
|
|
|
if (le) {
|
|
|
|
// append to the list
|
|
|
|
_pge_nextFreeGroup = le->next_entry;
|
|
|
|
GroupPGE *_ax = _pge_groupsTable[unk1];
|
|
|
|
_pge_groupsTable[unk1] = le;
|
|
|
|
le->next_entry = _ax;
|
|
|
|
le->index = idx;
|
|
|
|
le->group_id = unk2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Game::pge_removeFromInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3) {
|
|
|
|
pge2->unkF = 0xFF;
|
|
|
|
if (pge3 == pge1) {
|
|
|
|
pge3->current_inventory_PGE = pge2->next_inventory_PGE;
|
|
|
|
pge2->next_inventory_PGE = 0xFF;
|
|
|
|
} else {
|
|
|
|
pge1->next_inventory_PGE = pge2->next_inventory_PGE;
|
|
|
|
pge2->next_inventory_PGE = 0xFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_ZOrderByAnimY(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
|
|
|
|
if (pge1 != pge2) {
|
|
|
|
if (_res.getAniData(pge1->obj_type)[3] == comp) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_ZOrderByAnimYIfType(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
|
|
|
|
if (pge1->init_PGE->object_type == comp2) {
|
|
|
|
if (_res.getAniData(pge1->obj_type)[3] == comp) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_ZOrderIfIndex(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
|
|
|
|
if (pge1->index != comp2) {
|
|
|
|
pge_updateGroup(pge2->index, pge1->index, comp);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_ZOrderByIndex(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
|
|
|
|
if (pge1 != pge2) {
|
|
|
|
pge_updateGroup(pge2->index, pge1->index, comp);
|
|
|
|
_pge_compareVar1 = 0xFFFF;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_ZOrderByObj(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
|
|
|
|
if (comp == 10) {
|
|
|
|
if (pge1->init_PGE->object_type == comp && pge1->life >= 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (pge1->init_PGE->object_type == comp) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_ZOrderIfDifferentDirection(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
|
|
|
|
if (pge1 != pge2) {
|
|
|
|
if ((pge1->flags & 1) != (pge2->flags & 1)) {
|
|
|
|
_pge_compareVar1 = 1;
|
|
|
|
pge_updateGroup(pge2->index, pge1->index, comp);
|
|
|
|
if (pge2->index == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_ZOrderIfSameDirection(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
|
|
|
|
if (pge1 != pge2) {
|
|
|
|
if ((pge1->flags & 1) == (pge2->flags & 1)) {
|
|
|
|
_pge_compareVar2 = 1;
|
|
|
|
pge_updateGroup(pge2->index, pge1->index, comp);
|
|
|
|
if (pge2->index == 0) {
|
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_ZOrderIfTypeAndSameDirection(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
|
|
|
|
if (pge1->init_PGE->object_type == comp) {
|
|
|
|
if ((pge1->flags & 1) == (pge2->flags & 1)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_ZOrderIfTypeAndDifferentDirection(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
|
|
|
|
if (pge1->init_PGE->object_type == comp) {
|
|
|
|
if ((pge1->flags & 1) != (pge2->flags & 1)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Game::pge_ZOrderByNumber(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
|
|
|
|
return pge1 - pge2;
|
|
|
|
}
|