rockbox/apps/plugins/pacbox/pacbox.c
Franklin Wei 8bafa1bc9b pacbox: fix red and enable AI on all targets
Change-Id: I4275e4ec374f3b5a6839890e4a2aa0f577f56522
2019-07-28 21:03:38 -04:00

836 lines
32 KiB
C
Executable file

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Pacbox - a Pacman Emulator for Rockbox
*
* Based on PIE - Pacman Instructional Emulator
*
* Copyright (c) 1997-2003,2004 Alessandro Scotti
* http://www.ascotti.org/
* AI code (c) 2017 Moshe Piekarski
*
* ToDo convert all score to pinky location
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "plugin.h"
#include "arcade.h"
#include "pacbox.h"
#include "pacbox_lcd.h"
#include "wsg3.h"
#include "lib/configfile.h"
#include "lib/playback_control.h"
#include "lib/helper.h"
/*Allows split screen jump and makes pacman invincible if you start at 18 credits (for testing purposes)*/
//#define CHEATS 1
/* Enable AI on all targets */
#define AI 1
struct pacman_settings {
int difficulty;
int numlives;
int bonus;
int ghostnames;
int showfps;
int sound;
int ai;
};
static struct pacman_settings settings;
static struct pacman_settings old_settings;
static bool sound_playing = false;
#define SETTINGS_VERSION 2
#define SETTINGS_MIN_VERSION 2
#define SETTINGS_FILENAME "pacbox.cfg"
static char* difficulty_options[] = { "Normal", "Hard" };
static char* numlives_options[] = { "1", "2", "3", "5" };
static char* bonus_options[] = {"10000", "15000", "20000", "No Bonus"};
static char* ghostnames_options[] = {"Normal", "Alternate"};
static char* yesno_options[] = {"No", "Yes"};
#ifdef AI
static unsigned char ai_direction[15][205] = /* level turn directions */
{
{2,1,3,1,2,0,3,0,3,1,2,1,3,1,3,0,3,0,2,0,3,0,2,1,3,1,2,0,2,1,3,1,3,0,3,1,3,1,2,0,3,0,3,0,3,0,2,0,3,1,2,0,2,0,3,1,2,1,2,1,2,0,2,0,2,0,3,1,3,1,2,1,2,1,3,0,2,0,2,0,2,1,2,0,3,0,2,1,3,0,3,2,1,3,0,3,1,2,1,2,0,2,3,1,3,0,3,0,2,4}, /* first level */
{2,0,3,1,3,1,2,0,2,0,2,0,3,1,3,1,2,1,2,0,3,0,3,0,3,1,3,1,2,1,2,0,2,0,3,0,2,0,3,1,2,0,3,1,2,0,2,1,2,1,2,0,2,0,3,1,0,3,0,3,2,0,2,0,2,0,3,1,0,3,2,0,2,1,2,1,3,0,3,1,3,1,3,0,3,0,1,2,1,3,1,3,1,2},/* second level*/
{2,1,3,1,2,0,2,0,3,0,2,1,3,1,2,1,2,0,3,0,3,0,3,1,3,1,2,1,2,0,2,0,2,3,0,3,0,3,0,2,0,3,1,2,0,2,0,3,1,2,1,2,1,3,1,3,1,2,0,2,1,3,1,3,0,3,0,2,0,3,1,2,1,2,0,3,1,3,1,2,1,2,0,2,0,2,0,3,1,2,0,2,1,3,0,1,2,1,2,0},/*third level*/
{2,1,3,1,2,0,2,0,3,0,2,1,3,1,2,1,2,0,3,0,3,0,3,1,3,1,2,1,2,0,2,0,2,3,0,3,0,3,0,2,0,3,1,2,0,2,0,3,1,2,1,2,1,3,1,3,1,2,0,2,0,3,1,3,1,2,1,2,0,2,0,3,0,2,0,3,0,2,1,2,1,3,0,3,0,2,1,0,3,1,3,1,2,1,2,1,3,1,3,0,3,0,2,0,2,1,2,1,3,1,3,0,3,1,3,2,0,2,0,2,0,2},/*level four*/
{2,2,1,2,1,3,0,3,0,2,0,2,0,3,0,2,0,2,0,3,1,2,1,3,1,2,1,3,1,2,1,2,1,3,1,3,0,2,0,3,1,2,1,2,0,2,0,3,0,2,0,3,1,3,0,2,1,2,1,2,0,2,0,2,0,3,0,3,1,3,1,2,1,3,1,3,0,3,0,2,0,2,1,3,1,3,0,2,1,2,0,3,1,3,1,3,1,2,1,2,0,2,1,2,0,2,0,3,1,3,0,2,0,2},/*levels 5,7,8*/
{2,2,1,2,1,3,0,3,0,2,0,2,0,3,0,2,0,2,0,3,1,2,1,3,1,2,1,3,1,2,1,2,1,3,1,3,0,2,0,3,1,2,1,2,0,2,0,3,0,2,0,3,1,3,0,2,1,2,1,2,0,3,0,2,0,3,1,3,0,2,1,2,1,2,0,2,0,3,0,2,1,2,0,3,0,2,1,3,0,2,1,3,1,3,0,3,1,2,1,2,1,2,0,2,0,3,1,3,0,1,0,3,1},/*level six*/
{2,1,3,1,2,0,2,0,3,0,2,0,3,0,3,1,2,0,2,0,2,1,3,0,3,1,3,0,2,1,2,0,3,1,3,1,2,1,3,1,3,0,2,0,3,1,2,1,3,1,2,0,3,0,2,0,3,1,2,0,3,1,2,1,2,1,2,0,2,0,2,0,3,0,3,1,3,1,3,0,3,1,2,1,3,1,3,1,2,0,3,0,2,1,3,1,2,0,3,0,2,1,3,1,3,2,1,3,0,2,0,2,0,2,1,3,1,2,1,3,1,2,0,3,0,2,0,3,1,3,1,2,0,2,0,3,1,2,0,3,0,2,1,3,0,3,1,2,1,3,1,2,1,2,1,2,0,2,1,3,0,2,1,2,1,2,0,2,1,2,0,2,1,2,0,2,0,3,0,2,0,3,1,2,0,2,1,3,1,2,1,3,1,3,0,2},/*level nine*/
{2,2,1,2,1,3,0,3,0,2,0,2,0,3,0,2,0,2,0,3,1,2,1,3,1,2,1,3,1,2,1,2,1,3,1,3,0,2,0,3,1,2,1,2,0,2,0,3,0,2,0,3,1,3,0,2,1,2,1,2,0,3,0,2,0,3,1,2,0,2,1,2,1,2,1,2,0,2,0,3,0,2,1,3,0,3,0,1,2,1,3,1,2,1,0,3,0,2,0,2,1,2,0,2,0,3,1,3,0,2,1,2,1,2,0,3,0,2,0,3,1,2,0,2,1,2,1,2,1,2,0,2,0,3,0,2,1,3,0,3,0,1,2,1,3,1,2,1,0,3,0,2,0,2,1,2,0,2,0,3,1,3,0,2,1,2,1,2,},/*level ten*/
{2,1,3,1,2,0,2,0,3,0,2,0,3,0,3,1,2,0,2,0,2,1,3,0,3,1,3,0,2,1,2,0,3,1,3,1,2,1,3,1,3,0,2,0,3,1,2,1,3,1,2,0,3,0,2,0,3,1,2,0,3,1,2,1,2,1,2,0,2,0,2,0,3,0,3,1,3,1,3,0,3,1,2,1,3,1,3,1,2,0,3,0,2,1,3,1,2,0,3,0,2,1,3,1,3,2,1,3,0,2,0,2,0,2,1,3,1,2,1,3,1,2,0,3,0,2,0,3,1,3,1,2,0,2,0,3,1,2,0,3,0,2,1,3,0,1,3,0,3,1,3,0,2,1,3,1,3,1,2,1,2,1,3,0,2,1,2,1,2,0,2,0,2,0,3,1,2,1,2,1,2,0,2,0,2,0,3,1,3,1,2,0,2},/*level twelve*/
{2,2,1,2,1,3,0,3,0,2,0,2,0,3,0,2,0,2,0,3,1,2,1,3,1,2,1,3,1,2,1,2,1,3,1,3,0,2,0,3,1,2,1,2,0,2,0,3,0,2,0,3,1,2,0,3,1,2,0,2,3,1,2,1,2,1,3,1,2,0,2,0,2,0,3,1,2,1,2,1,2,0,2,1,2,1,3,1,3,0,3,0,3,0,3,0,2,1,3,1,3,1,3,0,3,0,2,0,3,0,2,1,3,0,3,1,3,2,1},/*level fourteen*/
{2,1,3,1,2,0,2,0,3,0,2,0,3,0,3,1,2,0,2,0,2,1,3,0,3,1,3,0,2,1,2,0,3,1,3,1,2,1,3,1,3,0,2,0,3,1,2,1,3,1,2,0,3,0,2,0,3,1,2,0,3,1,2,1,2,1,2,0,2,0,2,0,3,0,3,1,3,1,3,0,3,1,2,1,3,1,3,1,2,0,3,0,2,1,3,1,2,0,3,0,2,1,3,1,3,2,1,3,0,2,0,2,0,2,1,3,1,2,1,3,1,2,0,3,0,2,0,3,1,3,1,2,0,3,0,3,1,2,1,3,1,3,0,2,0,3,0,2,0,3,0,2,1,3,1,3,0,3,0,3,0,2,1,3,1,2,1,3,0,3,1,2,0,3,0,2,0,2,0,3,1,0,1,2,1,3,0,2,0,2,1,2,0,2,1,3,2},
{2,2,1,2,1,3,0,3,0,2,0,3,0,2,0,2,0,3,0,2,1,3,1,2,1,3,1,3,1,3,1,2,0,2,0,2,0,3,1,2,0,2,0,2,0,3,0,3,1,3,0,3,1,2,1,2,0,2,0,3,0,2,1,3,1,2,0,2,0,2,1,3,1,2,1,3,1,2,1,3,0,2,0,3,0,2},
{2,2,1,2,1,3,0,3,0,2,0,3,0,2,0,2,0,3,0,2,1,3,1,2,1,3,1,3,1,3,1,2,0,2,0,2,0,3,1,2,0,2,0,2,0,3,0,3,1,3,1,2,1,3,1,2,1,2,1,3,1,3,0,2,0,3,1,3,0,2,1,3,1,2,0,2,1,3,1,2,1,3,1,3,1,3,1,2,1},
{2,1,3,1,2,0,3,0,3,1,2,1,3,1,3,0,3,0,3,0,2,1,3,1,3,0,3,0,2,0,2,1,2,1,3,1,2,1,2,0,2,0,3,0,2,0,3,0,3,1,2,0,2,1,3,1,3,1,3,0,3,0,2,1,3,1,2,1,2,0,2,0,3,0,3,1,2,1,3,0},
{2,1,3,1,2,0,3,0,2,0,3,1,2,0,3,1,2,1,2,0,2,0,2,0,3,0,3,0,2,1,3,0,2,1,3,1,2,1,2,1,2,0,2,1,2,1,2,1,3,0,1,3,0,1,2,1,2,1,2,0,3,1,2}
};
static unsigned char ai_location[15][205] = /* level turn locations */
{
{0,52,58,57,61,34,60,37,54,43,55,42,58,43,61,46,60,49,57,48,54,49,51,42,52,43,55,39,54,34,55,34,58,37,107,52,58,57,61,48,60,49,57,52,42,57,39,48,35,57,40,54,39,48,35,52,37,51,40,48,43,45,42,42,39,39,35,43,37,49,40,48,43,42,49,49,45,45,42,42,39,39,40,34,39,43,35,34,40,37,39,223,39,55,52,54,57,55,57,58,54,57,119,52,58,55,57,57,54,53}, /* first level */
{0,47,54,52,58,57,61,45,60,42,57,39,54,43,55,49,58,48,61,34,60,37,54,40,51,49,52,57,55,57,58,54,54,51,48,52,39,48,35,57,40,54,39,57,40,54,35,48,37,40,56,37,57,33,54,37,125,54,40,51,170,42,48,39,39,34,35,37,174,39,220,45,35,39,37,34,40,37,39,40,40,43,43,46,42,49,16,40,48,42,48,45,52,55}, /*second level */
{0,52,58,57,61,45,60,42,57,43,54,39,55,49,58,48,61,34,60,37,54,40,51,49,52,57,55,57,58,54,54,51,51,139,49,48,52,42,57,39,48,35,57,40,54,39,48,35,52,37,51,40,48,43,49,46,52,55,39,54,34,55,34,58,37,57,43,54,42,51,49,52,48,55,39,35,43,37,49,40,48,43,45,42,42,39,39,35,43,37,39,35,34,37,43,127,37,39,40,34}, /*third level */
{0,52,58,57,61,45,60,42,57,43,54,39,55,49,58,48,61,34,60,37,54,40,51,49,52,57,55,57,58,54,54,51,51,71,49,48,52,42,57,39,48,35,57,40,54,39,48,35,52,37,51,40,48,43,49,46,52,55,45,54,39,48,40,49,49,52,48,55,45,54,42,45,43,42,42,39,43,35,39,37,34,40,37,39,43,35,34,151,35,37,37,49,40,48,43,42,52,43,55,46,54,49,51,42,48,39,52,34,55,34,58,37,54,43,55,87,45,54,42,48,39,88}, /*fourth level */
{0,14,39,58,34,61,46,60,49,57,45,54,42,45,43,42,42,39,39,35,43,37,42,40,43,43,42,49,49,52,48,55,42,58,43,61,57,60,54,54,57,55,57,58,54,57,48,54,52,39,48,35,52,37,57,35,54,46,51,49,42,48,39,42,34,39,37,35,43,37,49,40,48,43,49,46,52,42,57,39,39,35,34,37,40,105,43,35,39,40,34,39,40,40,43,43,49,52,48,55,45,54,39,58,36,57,34,54,37,55,43,54,39,51}, /*fifth level */
{0,184,39,58,34,61,46,60,49,57,45,54,42,45,43,42,42,39,39,35,43,37,42,40,43,43,42,49,49,52,48,55,42,58,43,61,57,60,54,54,57,55,57,58,54,57,48,54,52,39,48,35,52,37,57,35,54,46,51,49,42,45,43,42,42,39,52,40,57,39,51,40,48,43,45,42,42,39,43,35,39,40,34,39,37,35,34,37,37,35,34,200,37,46,40,45,49,49,42,52,39,58,36,57,34,54,37,55,52,119,54,48,37,},/*sixth level*/
{0,52,58,57,61,45,60,42,57,43,54,42,51,49,48,52,55,45,54,42,48,39,52,40,45,49,52,52,48,51,49,42,45,49,52,52,55,42,58,43,61,57,60,54,54,57,55,57,58,57,61,48,60,49,57,48,54,52,55,48,54,52,55,51,58,48,61,45,60,42,57,39,48,40,45,49,46,40,49,49,48,52,52,48,55,52,58,57,61,48,60,49,57,42,58,43,61,34,60,37,54,34,55,34,58,118,34,61,43,60,42,57,39,48,54,52,57,55,57,58,57,61,48,60,49,57,39,48,40,52,43,55,39,42,34,39,37,40,34,39,37,35,34,37,37,35,43,37,42,40,43,43,42,46,51,49,42,48,39,55,52,48,51,49,42,52,39,48,54,52,51,51,42,52,39,48,54,42,57,39,54,35,57,37,141,35,48,37,49,40,48,43,49,46,52,39},/*ninth level*/
{0,14,39,58,34,61,46,60,49,57,45,54,42,45,43,42,42,39,39,35,43,37,42,40,43,43,42,49,49,52,48,55,42,58,43,61,57,60,54,54,57,55,57,58,54,57,48,54,52,39,48,35,52,37,57,35,54,46,51,49,42,45,43,42,42,39,57,40,54,39,51,40,48,43,42,46,39,42,34,39,37,35,34,40,37,39,43,205,37,42,40,43,43,42,73,45,43,42,42,39,39,58,36,57,34,54,37,55,52,48,51,49,42,52,45,60,},/*tenth level*/
{0,52,58,57,61,45,60,42,57,43,54,42,51,49,48,52,55,45,54,42,48,39,52,40,45,49,52,52,48,51,49,42,45,49,52,52,55,42,58,43,61,57,60,54,54,57,55,57,58,57,61,48,60,49,57,48,54,52,55,48,54,52,55,51,58,48,61,45,60,42,57,39,48,40,45,49,46,40,49,49,48,52,52,48,55,52,58,57,61,48,60,49,57,42,58,43,61,34,60,37,54,34,55,34,58,94,34,61,43,60,42,57,39,48,54,52,57,55,57,58,57,61,48,60,49,57,39,48,40,52,43,55,39,42,34,39,37,40,34,39,37,35,34,37,37,191,46,40,45,49,46,52,39,42,40,43,43,49,49,42,52,39,55,52,48,51,49,42,52,39,48,54,39,48,35,57,37,51,40,48,43,45,42,42,39,39,35,43,37,57,40,54,35},/*twelfth level*/
{0,14,39,58,34,61,46,60,49,57,45,54,42,45,43,42,42,39,39,35,43,37,42,40,43,43,42,49,49,52,48,55,42,58,43,61,57,60,54,54,57,55,57,58,54,57,48,54,52,39,48,35,52,37,48,35,57,37,54,35,104,52,37,51,40,48,43,49,49,42,48,39,42,34,39,57,40,54,46,51,49,42,48,39,52,34,55,34,58,37,54,40,51,49,48,52,39,42,40,43,43,49,52,52,48,37,42,34,39,37,35,34,37,37,35,43,37,127,39},/*fourteenth level*/
{0,52,58,57,61,45,60,42,57,43,54,42,51,49,48,52,55,45,54,42,48,39,52,40,45,45,52,52,48,51,49,42,45,49,52,52,55,42,58,43,61,57,60,54,54,57,55,57,58,57,61,48,60,49,57,48,54,52,55,48,54,52,55,51,58,48,61,45,60,42,57,39,48,40,45,49,46,40,49,49,48,52,52,48,55,52,58,57,61,48,60,49,57,42,58,43,61,34,60,37,54,34,55,34,58,124,34,61,43,60,42,57,39,48,54,52,57,55,57,58,57,61,48,60,49,57,39,48,40,52,43,55,39,54,40,45,49,49,42,52,43,55,52,54,51,48,37,41,34,39,37,35,34,37,37,46,40,45,46,42,49,39,42,40,43,43,42,49,49,48,52,55,39,54,40,48,54,39,48,35,57,159,173,37,54,40,57,39,54,35,48,37,45,35,39,37,40},
{0,14,39,58,34,61,46,60,49,57,48,54,52,48,51,45,48,42,49,39,42,40,43,43,42,49,49,52,52,58,57,61,45,60,42,57,39,39,52,55,45,54,39,42,34,39,37,35,43,37,46,35,52,46,51,49,42,48,54,42,57,35,54,37,57,40,54,39,45,35,34,40,37,52,34,55,34,58,34,61,57,60,57,57,57,53},
{0,14,39,58,34,61,46,60,49,57,48,54,52,48,51,45,48,42,49,39,42,40,43,43,42,49,49,52,52,58,57,61,45,60,42,57,39,39,52,55,45,54,39,42,34,39,37,35,43,37,49,40,48,43,49,49,42,52,34,55,34,58,37,42,34,35,43,37,57,35,54,37,57,40,54,35,48,37,49,40,48,43,49,46,52,52,57,55,57},
{0,52,58,57,61,34,60,37,54,43,55,42,58,43,61,46,60,49,57,52,54,48,55,52,58,55,57,57,54,54,48,51,49,42,52,43,55,39,58,36,57,34,54,37,42,34,39,37,35,43,37,39,35,34,37,37,46,40,49,49,48,52,35,48,37,49,40,48,43,45,42,42,39,52,35,57,37,54,40,57},
{0,52,58,57,61,48,60,49,57,48,54,52,55,48,54,57,55,57,58,54,48,51,45,48,42,49,39,52,35,48,37,52,34,48,37,52,46,51,49,45,37,39,33,38,35,35,49,34,60,37,53,60,42,55,60,38,35,58,40,54,35,57,37}
};
#endif
static struct configdata config[] =
{
{TYPE_ENUM, 0, 2, { .int_p = &settings.difficulty }, "Difficulty",
difficulty_options},
{TYPE_ENUM, 0, 4, { .int_p = &settings.numlives }, "Pacmen Per Game",
numlives_options},
{TYPE_ENUM, 0, 4, { .int_p = &settings.bonus }, "Bonus", bonus_options},
{TYPE_ENUM, 0, 2, { .int_p = &settings.ghostnames }, "Ghost Names",
ghostnames_options},
{TYPE_ENUM, 0, 2, { .int_p = &settings.showfps }, "Show FPS",
yesno_options},
{TYPE_ENUM, 0, 2, { .int_p = &settings.sound }, "Sound",
yesno_options},
#ifdef AI
{TYPE_ENUM, 0, 2, { .int_p = &settings.ai }, "AI",
yesno_options},
#endif
};
static bool loadFile( const char * name, unsigned char * buf, int len )
{
char filename[MAX_PATH];
rb->snprintf(filename,sizeof(filename), ROCKBOX_DIR "/pacman/%s",name);
int fd = rb->open( filename, O_RDONLY);
if( fd < 0 ) {
return false;
}
int n = rb->read( fd, buf, len);
rb->close( fd );
if( n != len ) {
return false;
}
return true;
}
static bool loadROMS( void )
{
bool romsLoaded = false;
romsLoaded = loadFile( "pacman.6e", ram_, 0x1000) &&
loadFile( "pacman.6f", ram_+0x1000, 0x1000) &&
loadFile( "pacman.6h", ram_+0x2000, 0x1000) &&
loadFile( "pacman.6j", ram_+0x3000, 0x1000) &&
loadFile( "pacman.5e", charset_rom_, 0x1000) &&
loadFile( "pacman.5f", spriteset_rom_, 0x1000);
if( romsLoaded ) {
decodeROMs();
reset_PacmanMachine();
}
return romsLoaded;
}
/* A buffer to render Pacman's 244x288 screen into */
static unsigned char video_buffer[ScreenWidth*ScreenHeight] __attribute__ ((aligned (16)));
static long start_time;
static long video_frames = 0;
static int dipDifficulty[] = { DipDifficulty_Normal, DipDifficulty_Hard };
static int dipLives[] = { DipLives_1, DipLives_2, DipLives_3, DipLives_5 };
static int dipBonus[] = { DipBonus_10000, DipBonus_15000, DipBonus_20000,
DipBonus_None };
static int dipGhostNames[] = { DipGhostNames_Normal, DipGhostNames_Alternate };
static int settings_to_dip(struct pacman_settings settings)
{
return ( DipPlay_OneCoinOneGame |
DipCabinet_Upright |
DipMode_Play |
DipRackAdvance_Off |
dipDifficulty[settings.difficulty] |
dipLives[settings.numlives] |
dipBonus[settings.bonus] |
dipGhostNames[settings.ghostnames]
);
}
static bool pacbox_menu(void)
{
int selected=0;
int result;
int menu_quit=0;
int new_setting;
bool need_restart = false;
static const struct opt_items noyes[2] = {
{ "No", -1 },
{ "Yes", -1 },
};
static const struct opt_items difficulty_options[2] = {
{ "Normal", -1 },
{ "Harder", -1 },
};
static const struct opt_items numlives_options[4] = {
{ "1", -1 },
{ "2", -1 },
{ "3", -1 },
{ "5", -1 },
};
static const struct opt_items bonus_options[4] = {
{ "10000 points", -1 },
{ "15000 points", -1 },
{ "20000 points", -1 },
{ "No bonus", -1 },
};
static const struct opt_items ghostname_options[2] = {
{ "Normal", -1 },
{ "Alternate", -1 },
};
enum
{
PBMI_DIFFICULTY = 0,
PBMI_PACMEN_PER_GAME,
PBMI_BONUS_LIFE,
PBMI_GHOST_NAMES,
PBMI_DISPLAY_FPS,
PBMI_SOUND,
#ifdef AI
PBMI_AI,
#endif
PBMI_RESTART,
PBMI_QUIT,
};
MENUITEM_STRINGLIST(menu, "Pacbox Menu", NULL,
"Difficulty", "Pacmen Per Game", "Bonus Life",
"Ghost Names", "Display FPS", "Sound",
#ifdef AI
"AI",
#endif
"Restart", "Quit");
rb->button_clear_queue();
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_RGB565);
#endif
while (!menu_quit) {
result=rb->do_menu(&menu, &selected, NULL, false);
switch(result)
{
case PBMI_DIFFICULTY:
new_setting=settings.difficulty;
rb->set_option("Difficulty", &new_setting, INT,
difficulty_options , 2, NULL);
if (new_setting != settings.difficulty) {
settings.difficulty=new_setting;
need_restart=true;
}
break;
case PBMI_PACMEN_PER_GAME:
new_setting=settings.numlives;
rb->set_option("Pacmen Per Game", &new_setting, INT,
numlives_options , 4, NULL);
if (new_setting != settings.numlives) {
settings.numlives=new_setting;
need_restart=true;
}
break;
case PBMI_BONUS_LIFE:
new_setting=settings.bonus;
rb->set_option("Bonus Life", &new_setting, INT,
bonus_options , 4, NULL);
if (new_setting != settings.bonus) {
settings.bonus=new_setting;
need_restart=true;
}
break;
case PBMI_GHOST_NAMES:
new_setting=settings.ghostnames;
rb->set_option("Ghost Names", &new_setting, INT,
ghostname_options , 2, NULL);
if (new_setting != settings.ghostnames) {
settings.ghostnames=new_setting;
need_restart=true;
}
break;
case PBMI_DISPLAY_FPS:
rb->set_option("Display FPS",&settings.showfps,INT,
noyes, 2, NULL);
break;
case PBMI_SOUND:
rb->set_option("Sound",&settings.sound, INT,
noyes, 2, NULL);
break;
#ifdef AI
case PBMI_AI:
rb->set_option("AI",&settings.ai, INT,
noyes, 2, NULL);
break;
#endif
case PBMI_RESTART:
need_restart=true;
menu_quit=1;
break;
default:
menu_quit=1;
break;
}
}
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_PAL256);
#endif
if (need_restart) {
init_PacmanMachine(settings_to_dip(settings));
}
/* Possible results:
exit game
restart game
usb connected
*/
return (result==PBMI_QUIT);
}
/* Sound is emulated in ISR context, so not much is done per sound frame */
#define NBSAMPLES 128
static uint32_t sound_buf[NBSAMPLES];
#if CONFIG_CPU == MCF5249
/* Not enough to put this in IRAM */
static int16_t raw_buf[NBSAMPLES];
#else
static int16_t raw_buf[NBSAMPLES] IBSS_ATTR;
#endif
/*
Audio callback
*/
static void get_more(const void **start, size_t *size)
{
int32_t *out, *outend;
int16_t *raw;
/* Emulate the audio for the current register settings */
playSound(raw_buf, NBSAMPLES);
out = sound_buf;
outend = out + NBSAMPLES;
raw = raw_buf;
/* Convert to stereo */
do
{
uint32_t sample = (uint16_t)*raw++;
*out++ = sample | (sample << 16);
}
while (out < outend);
*start = sound_buf;
*size = NBSAMPLES*sizeof(sound_buf[0]);
}
/*
Start the sound emulation
*/
static void start_sound(void)
{
int sr_index;
if (sound_playing)
return;
#ifndef PLUGIN_USE_IRAM
/* Ensure control of PCM - stopping music itn't obligatory */
rb->plugin_get_audio_buffer(NULL);
#endif
/* Get the closest rate >= to what is preferred */
sr_index = rb->round_value_to_list32(PREFERRED_SAMPLING_RATE,
rb->hw_freq_sampr, HW_NUM_FREQ, false);
if (rb->hw_freq_sampr[sr_index] < PREFERRED_SAMPLING_RATE
&& sr_index > 0)
{
/* Round up */
sr_index--;
}
wsg3_set_sampling_rate(rb->hw_freq_sampr[sr_index]);
rb->pcm_set_frequency(rb->hw_freq_sampr[sr_index]);
rb->pcm_play_data(get_more, NULL, NULL, 0);
sound_playing = true;
}
/*
Stop the sound emulation
*/
static void stop_sound(void)
{
if (!sound_playing)
return;
rb->pcm_play_stop();
rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
sound_playing = false;
}
/* use buttons for joystick */
void joystick(void)
{
int status;
/* Check the button status */
status = rb->button_status();
rb->button_clear_queue();
/*handle buttons if AI is off */
#ifdef PACMAN_HAS_REMOTE
setDeviceMode( Joy1_Left, (status & PACMAN_LEFT || status == PACMAN_RC_LEFT) ? DeviceOn : DeviceOff);
setDeviceMode( Joy1_Right, (status & PACMAN_RIGHT || status == PACMAN_RC_RIGHT) ? DeviceOn : DeviceOff);
setDeviceMode( Joy1_Up, (status & PACMAN_UP || status == PACMAN_RC_UP) ? DeviceOn : DeviceOff);
setDeviceMode( Joy1_Down, (status & PACMAN_DOWN || status == PACMAN_RC_DOWN) ? DeviceOn : DeviceOff);
setDeviceMode( CoinSlot_1, (status & PACMAN_COIN || status == PACMAN_RC_COIN) ? DeviceOn : DeviceOff);
setDeviceMode( Key_OnePlayer, (status & PACMAN_1UP || status == PACMAN_RC_1UP) ? DeviceOn : DeviceOff);
setDeviceMode( Key_TwoPlayers, (status & PACMAN_2UP || status == PACMAN_RC_2UP) ? DeviceOn : DeviceOff);
#else
setDeviceMode( Joy1_Left, (status & PACMAN_LEFT) ? DeviceOn : DeviceOff);
setDeviceMode( Joy1_Right, (status & PACMAN_RIGHT) ? DeviceOn : DeviceOff);
setDeviceMode( Joy1_Up, (status & PACMAN_UP) ? DeviceOn : DeviceOff);
setDeviceMode( Joy1_Down, (status & PACMAN_DOWN) ? DeviceOn : DeviceOff);
setDeviceMode( CoinSlot_1, (status & PACMAN_COIN) ? DeviceOn : DeviceOff);
setDeviceMode( Key_OnePlayer, (status & PACMAN_1UP) ? DeviceOn : DeviceOff);
#ifdef PACMAN_2UP
setDeviceMode( Key_TwoPlayers, (status & PACMAN_2UP) ? DeviceOn : DeviceOff);
#endif
#endif
#ifdef CHEATS
// skip level for testing purposes
if(status == SKIP_LEVEL)
{
//dots
ram_[0x4E0E] = 242;
//level
ram_[0x4E13] = 254;
}
#endif
}
#ifdef AI
/* blank controls */
void clear_joystick(void)
{
setDeviceMode( Joy1_Left, DeviceOff);
setDeviceMode( Joy1_Right, DeviceOff);
setDeviceMode( Joy1_Up, DeviceOff);
setDeviceMode( Joy1_Down, DeviceOff);
}
/* Make turns */
void ai_turn( unsigned char level, unsigned char turn)
{
switch(ai_direction[level][turn])
{
case 0:
clear_joystick();
setDeviceMode( Joy1_Up, DeviceOn);
break;
case 1:
clear_joystick();
setDeviceMode( Joy1_Down, DeviceOn);
break;
case 2:
clear_joystick();
setDeviceMode( Joy1_Right, DeviceOn);
break;
case 3:
clear_joystick();
setDeviceMode( Joy1_Left, DeviceOn);
break;
case 4:
clear_joystick();
break;
}
}
/*
Decide turns automatically
*/
unsigned char ai( unsigned char turn )
{
unsigned char position; /* pac-mans current position */
unsigned char score = ram_[0x4E81]; /* current score */
unsigned char level; /* current game level */
unsigned char map[20] = {0,1,2,3,4,5,4,4,6,7,4,8,8,9,10,10,11,10,12,12};
/*Select level map*/
if(ram_[0x4E13] < 20)
{
level=map[ram_[0x4E13]];
}else if(ram_[0x4E13] != 255)
level=13;
else
level=14;
if((level == 4) || (level == 8) || (level == 10))
{
switch(ram_[0x4E13])
{
case 4:
ai_location[4][85] = 105;
break;
case 6:
ai_location[4][85] = 143;
break;
case 7:
ai_location[4][85] = 91;
break;
case 10:
ai_location[4][85] = 217;
break;
case 11:
ai_location[8][105] = 94;
ai_location[8][145] = 191;
break;
case 12:
ai_location[8][105] = 138;
ai_location[8][145] = 75;
break;
case 14:
ai_location[10][105] = 124;
ai_location[10][181] = 159;
ai_location[10][182] = 173;
break;
case 15:
ai_location[10][105] = 200;
ai_location[10][181] = 75;
ai_location[10][182] = 89;
break;
case 17:
ai_location[10][105] = 154;
ai_location[10][181] = 189;
ai_location[10][182] = 203;
default:
break;
}
}
/* AI can't start in middle of a level */
if( turn > 200)
{
rb->splash(HZ/2, "AI will engage at next level start");
return 0;
}
if( turn == 0)
{
if( (ram_[0x4E0E] == 1) && (ram_[0x4D3A] == 47))
{
turn++;
}
joystick();
return turn;
}
if(turn != 0)
{
/* set which axis to look for pac-man along */
position = ram_[0x4D3A];
if( ai_direction[level][turn-1] < 2)
{
position = ram_[0x4D39];
}
/*move joystick if necessary */
if(ai_location[level][turn] < 70)
{
if(ai_location[level][turn] < 30) /* handle turns using pinky's location as basis for turn timing */
{
if( ram_[0x4D31] == (ai_location[level][turn] + 30))
{
ai_turn(level,turn);
turn++;
}
}else if( position == ai_location[level][turn] ) /* handle turns using pacman's location as basis for turn timing */
{
ai_turn(level,turn);
turn++;
}
}else /* handle turns on eating ghost after center of tile using score as basis for turn timing */
{
if( score == (ai_location[level][turn]-70))
{
ai_turn(level,turn);
turn++;
}
}
}
/* reset turn counter and joystick direction on level start */
if(ram_[0x4E0E] == 0 )
{
/*levels that start facing right */
if((level != 4) && (level != 11) && (level != 7) && (level != 5) && (level != 9) && (level !=12))
clear_joystick();
return 1;
}
return turn;
}
#endif
/*
Runs the game engine for one frame.
*/
static int gameProc( void )
{
int fps;
int status;
long end_time;
int frame_counter = 0;
int yield_counter = 0;
#ifdef AI
unsigned char turn = 250;
#endif
if (settings.sound)
start_sound();
while (1)
{
/* Run the machine for one frame (1/60th second) */
run();
/*Make Pac-man invincible*/
#ifdef CHEATS
if(ram_[0x4E6E]== 23)
ram_[0x4DA5]=00;
#endif
frame_counter++;
/* Check the button status */
status = rb->button_status();
rb->button_clear_queue();
#ifdef HAS_BUTTON_HOLD
if (rb->button_hold())
status = PACMAN_MENU;
#endif
if ((status & PACMAN_MENU) == PACMAN_MENU
#ifdef PACMAN_RC_MENU
|| status == PACMAN_RC_MENU
#endif
) {
bool menu_res;
end_time = *rb->current_tick;
stop_sound();
menu_res = pacbox_menu();
rb->lcd_clear_display();
#ifdef HAVE_REMOTE_LCD
rb->lcd_remote_clear_display();
rb->lcd_remote_update();
#endif
if (menu_res)
return 1;
if (settings.sound)
start_sound();
start_time += *rb->current_tick-end_time;
}
#ifdef AI
if(!settings.ai)
{
joystick();
turn = 250;
}
/* run ai */
if (settings.ai)
turn = ai(turn);
#else
joystick();
#endif
/* We only update the screen every third frame - Pacman's native
framerate is 60fps, so we are attempting to display 20fps */
if (frame_counter == 60 / FPS) {
frame_counter = 0;
video_frames++;
yield_counter ++;
if (yield_counter == FPS) {
yield_counter = 0;
rb->yield ();
}
/* The following functions render the Pacman screen from the
contents of the video and color ram. We first update the
background, and then draw the Sprites on top.
*/
renderBackground( video_buffer );
renderSprites( video_buffer );
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_blit_pal256( video_buffer, 0, 0, XOFS, YOFS,
ScreenWidth, ScreenHeight);
#else
blit_display(rb->lcd_framebuffer,video_buffer);
#endif
if (settings.showfps) {
fps = (video_frames*HZ*100) / (*rb->current_tick-start_time);
rb->lcd_putsxyf(0,0,"%d.%02d / %d fps ",fps/100,fps%100,FPS);
}
#if !defined(HAVE_LCD_MODES) || \
defined(HAVE_LCD_MODES) && !(HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_update();
#endif
/* Keep the framerate at Pacman's 60fps */
end_time = start_time + (video_frames*HZ)/FPS;
while (TIME_BEFORE(*rb->current_tick,end_time)) {
rb->sleep(1);
}
}
}
stop_sound();
return 0;
}
enum plugin_status plugin_start(const void* parameter)
{
(void)parameter;
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(true);
#endif
rb->lcd_set_backdrop(NULL);
rb->lcd_set_foreground(LCD_WHITE);
rb->lcd_set_background(LCD_BLACK);
rb->lcd_clear_display();
rb->lcd_update();
/* Set the default settings */
settings.difficulty = 0; /* Normal */
settings.numlives = 2; /* 3 lives */
settings.bonus = 0; /* 10000 points */
settings.ghostnames = 0; /* Normal names */
settings.showfps = 0; /* Do not show FPS */
settings.sound = 0; /* Sound off by default */
settings.ai = 0; /* AI off by default */
if (configfile_load(SETTINGS_FILENAME, config,
sizeof(config)/sizeof(*config),
SETTINGS_MIN_VERSION
) < 0)
{
/* If the loading failed, save a new config file (as the disk is
already spinning) */
configfile_save(SETTINGS_FILENAME, config,
sizeof(config)/sizeof(*config),
SETTINGS_VERSION);
}
/* Keep a copy of the saved version of the settings - so we can check if
the settings have changed when we quit */
old_settings = settings;
/*Turn off backlight for ai*/
if(settings.ai)
backlight_ignore_timeout();
/* Initialise the hardware */
init_PacmanMachine(settings_to_dip(settings));
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_PAL256);
#endif
/* Load the romset */
if (loadROMS()) {
start_time = *rb->current_tick-1;
gameProc();
/* Save the user settings if they have changed */
if (rb->memcmp(&settings,&old_settings,sizeof(settings))!=0) {
rb->splash(0, "Saving settings...");
configfile_save(SETTINGS_FILENAME, config,
sizeof(config)/sizeof(*config),
SETTINGS_VERSION);
}
} else {
rb->splashf(HZ*2, "No ROMs in %s/pacman/", ROCKBOX_DIR);
}
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_RGB565);
#endif
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
backlight_use_settings();
return PLUGIN_OK;
}//