// Changes to particular games, as PLAN.md's decisions have them: copy // protection is skipped (decision 3) and the script bugs Sierra shipped are // patched (decision 2). A game is known by its v3 file prefix (agiRes.c). #include "agi.h" #include "joey/core.h" // Gold Rush's clock: logic 0 counts v166 cycles to a second. At a change // of speed it copies v166 from v167 (Fastest) or v165 (Fast), which the // speed test in logic 129 sets: it counts a second of empty cycles at // Fastest and takes a third and a quarter of them (at most 20) for the // rate a playing cycle would run at on a 1988 PC. Here Fastest is held to // the frame rate, playing or not, so the clock would run six times too // fast; and the cracked copy (games/gr) lost the test with logic 129, so // its clock stops. Both are filled with the real rates every cycle: Fast // is one cycle a unit, Fastest one a frame (agiIdle waits for each). #define GR_FAST_CPS_VAR 165u #define GR_FASTEST_CPS_VAR 167u #define GR_FAST_CPS (AGI_MS_PER_SECOND / AGI_CYCLE_UNIT_MS) // King's Quest IV's graveyards (rooms 16 and 18): three zombies rise at // night, each one's distance from Rosella measured into v221..v223 and its // state kept in v155, v156, and v162 (room 16) or v158 (room 18). #define KQ4_GRAVEYARD_A 16u #define KQ4_GRAVEYARD_B 18u #define KQ4_ZOMBIE_DIST 221u #define KQ4_ZOMBIES 3u #define KQ4_ZOMBIE_CHASING 2u // The copy protection (decision 3): the rooms in which a game asks a // question from its manual, and the room a right answer goes on to. typedef struct { AgiGameIdE game; uint8_t first; uint8_t last; uint8_t after; } QuestionRoomsT; // ----- Tables ----- static const uint8_t kKq4ZombieStateA[KQ4_ZOMBIES] = { 155u, 156u, 162u }; static const uint8_t kKq4ZombieStateB[KQ4_ZOMBIES] = { 155u, 156u, 158u }; static const QuestionRoomsT kQuestionRooms[] = { // King's Quest IV asks a word from its manual in one of rooms 141 to // 143; the title, room 96, follows. { AGI_GAME_KQ4, 141u, 143u, 96u }, // Gold Rush's claim jumper asks about the book "California Gold" on // the way from the title (logic 129 to room 125, which picks one of // rooms 126 to 128); room 73 follows. The cracked copy's logic 129 is // the original's logic 73 and never comes here. { AGI_GAME_GOLD_RUSH, 125u, 125u, 73u } }; // ----- Public API (alphabetical) ----- // Before each cycle's logic 0. void agiPatchCycle(void) { if (gAgi->game.id != AGI_GAME_GOLD_RUSH) { return; } gAgi->vars[GR_FAST_CPS_VAR] = (uint8_t)GR_FAST_CPS; gAgi->vars[GR_FASTEST_CPS_VAR] = (uint8_t)jlFrameHz(); } // The distance the distance command reports into var. King's Quest IV's // zombie bug (in Sierra's interpreter too): with the obsidian scarab, a // zombie rising beside Rosella is turned away before it is up, so it // rises and never chases. Patched as ScummVM does: until a zombie chases, // it reads as far away. uint8_t agiPatchDistance(uint8_t var, uint8_t distance) { uint8_t room; uint8_t zombie; room = gAgi->vars[VAR_ROOM]; if (gAgi->game.id != AGI_GAME_KQ4 || (room != KQ4_GRAVEYARD_A && room != KQ4_GRAVEYARD_B) || var < KQ4_ZOMBIE_DIST || var >= KQ4_ZOMBIE_DIST + KQ4_ZOMBIES) { return distance; } zombie = (uint8_t)(var - KQ4_ZOMBIE_DIST); if (gAgi->vars[(room == KQ4_GRAVEYARD_A) ? kKq4ZombieStateA[zombie] : kKq4ZombieStateB[zombie]] != KQ4_ZOMBIE_CHASING) { return AGI_DISTANCE_FAR; } return distance; } // The room new.room really goes to: a manual question is skipped. uint8_t agiPatchRoom(uint8_t room) { const QuestionRoomsT *q; uint8_t i; for (i = 0u; i < sizeof(kQuestionRooms) / sizeof(kQuestionRooms[0]); i++) { q = &kQuestionRooms[i]; if (gAgi->game.id == q->game && room >= q->first && room <= q->last) { return q->after; } } return room; }