📄 mainwin.cpp
字号:
//
// Script player主窗口(MainWindow)
//
// Copyright (c) 2000-2002 Chihiro.SAKAMOTO (HyperWorks)
//
#include "StdAfx.h"
#include "ScrPlay.h"
#include "Window.h"
#include "MainWin.h"
#include "AboutDlg.h"
#include "Misc.h"
#include "dc.h"
#include "resource.h"
// CG的显示位置
const CRect CMainWin::Position[] = {
CRect(0, 0, 0, 0), // None
CRect(0, 0, WindowWidth / 2, WindowHeight), // Left
CRect(WindowWidth / 2, 0, WindowWidth, WindowHeight), // Right
CRect(0, 0, WindowWidth, WindowHeight), // Both
CRect(WindowWidth / 4, 0, WindowWidth * 3 / 4, WindowHeight), // Center
} ;
//
// 构造函数
//
// 变量的初始化
// 当窗口由OnCreate初始化时,没有带出任何东西
//
CMainWin::CMainWin():
TextRect(MSG_X, MSG_Y, MSG_X + MSG_W, MSG_Y + MSG_H),
WaitMarkRect(MsgX(WAITMARK_X), MsgY(WAITMARK_Y),
MsgX(WAITMARK_X) + MessageFont, MsgY(WAITMARK_Y) + MessageFont),
InvalidRect(0, 0, 0, 0)
{
CurX = CurY = 0;
OverlapFlags = 0;
BgColor = BlackPixel;
TextDisplay = FALSE;
WaitMarkShowing = FALSE;
OverlapBounds.SetRectEmpty();
BackShow = FALSE;
OverlapShow = FALSE;
TextShow = FALSE;
MenuShow = FALSE;
Action = &NopAction;
hFont = 0;
}
//
// 析构函数
//
CMainWin::~CMainWin()
{
if (hFont) {
DeleteObject(hFont);
hFont = 0;
}
}
//
// 窗口制作的前置处理
//
// 指定样式与大小
//
BOOL CMainWin::PreCreateWindow(CREATESTRUCT &cs)
{
cs.dwExStyle = WS_EX_CLIENTEDGE;
cs.style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
CRect rect(0, 0, WindowWidth, WindowHeight);
::AdjustWindowRectEx(&rect, cs.style, TRUE, cs.dwExStyle);
int width = rect.Width();
int height = rect.Height();
CRect rcArea;
SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcArea, NULL);
int x = rcArea.left + (rcArea.Width() - width) / 2;
int y = rcArea.top + (rcArea.Height() - height) / 2;
cs.x = x;
cs.y = y;
cs.cx = width;
cs.cy = height;
cs.lpszClass = "MainWindow";
// 指定CS_OWNDC,以提高GetDC的速度
if (!Application->RegisterWndClass(cs.lpszClass,
CS_VREDRAW | CS_HREDRAW | CS_OWNDC, LoadCursor(NULL, IDC_ARROW),
(HBRUSH)::GetStockObject(BLACK_BRUSH), Application->LoadIcon(IDC_APPICON)))
return FALSE;
return TRUE;
}
//
// 消息处理
//
LRESULT CMainWin::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_FIRSTACTION: // 盘一个送来的消息
OnFirstAction();
break;
case WM_CLOSE: // 关闭窗口
OnClose();
break;
case WM_ERASEBKGND: // 删除背景
return FALSE; // 什么都不做
case WM_LBUTTONDOWN: // 按下鼠标左键
Action->LButtonDown(wParam, CPoint(lParam));
break;
case WM_LBUTTONUP: // 放开鼠标左键
Action->LButtonUp(wParam, CPoint(lParam));
break;
case WM_RBUTTONDOWN: // 按下鼠标右键
Action->RButtonDown(wParam, CPoint(lParam));
break;
case WM_RBUTTONUP: // 放开鼠标右键
Action->RButtonUp(wParam, CPoint(lParam));
break;
case WM_MOUSEMOVE: // 移动鼠标
Action->MouseMove(wParam, CPoint(lParam));
break;
case WM_KEYDOWN: // 键盘事件
Action->KeyDown(wParam);
break;
default:
return CWindow::WindowProc(uMsg, wParam, lParam);
}
return 0L;
}
//
// IDLE处理
//
BOOL CMainWin::OnIdle(long count)
{
return Action->IdleAction();
}
//
// WM_CREATE的处理
//
BOOL CMainWin::OnCreate(CREATESTRUCT *cs)
{
LoadAccelTable(IDC_APPACCEL);
CClientDC dc(this);
// 配置图片区域
if (!MixedImage.Create(dc, WindowWidth, WindowHeight)
|| !BackLayer.Create(WindowWidth, WindowHeight)
|| !OverlapLayer.Create(WindowWidth, WindowHeight)) {
MessageBox("内存无法配置。\n"
"请先关闭其他应用程序,在重新执行这个程序。");
return FALSE;
}
// 显示用图像的清除
MixedImage.Clear();
// 事先建立字型
if ((hFont = CreateFont(-MessageFont, 0, 0, 0, MessageStyle, FALSE, FALSE, FALSE,
GB2312_CHARSET, OUT_DEFAULT_PRECIS, CLIP_CHARACTER_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "宋体")) == 0) {
MessageBox("找不到宋体。");
return FALSE;
}
SetAction(ActionNop);
// 送出盘一个消息作为开端
while (!PostMessage(WM_FIRSTACTION)) {
// PostMessage 当伫列满时会错误故重复送出
#ifdef _DEBUG
TRACE("PostMessage Error code = %d\n", GetLastError());
#endif
::Sleep(110); // 重新送出消息时要稍作等待
}
return TRUE;
}
//
// 开始时盘一个动作
//
void CMainWin::OnFirstAction()
{
// 一开始进行的是“主菜单”
StartMainMenu();
}
//
// WM_CLOSE的处理
//
void CMainWin::OnClose()
{
if (MessageBox("确定要结束了吗?", ApplicationTitle,
MB_ICONQUESTION|MB_OKCANCEL) == IDOK) {
::DestroyWindow(hWnd);
}
}
//
// WM_PAINT的处理(再绘制)
//
void CMainWin::OnPaint()
{
CPaintDC dc(this);
MixedImage.Draw(dc, dc.ps.rcPaint);
}
//
// WM_COMMAND的处理(Menu的处理)
//
void CMainWin::OnCommand(UINT notifyCode, UINT id, HWND ctrl)
{
switch (id) {
case ID_APP_EXIT: // 结束
SendMessage(WM_CLOSE);
break;
case ID_APP_ABOUT: // 版本消息
CAboutDlg().DoModal(IDD_ABOUT, hWnd);
break;
case ID_STOPSCRIPT: // 中断剧情
if (IsStopScript() && MessageBox("您确定要停止游戏吗?", ApplicationTitle,
MB_ICONQUESTION|MB_OKCANCEL) == IDOK)
ScriptAction.Abort();
break;
default:
break;
}
}
//
// 功能表初始化
//
void CMainWin::OnInitSubMenu(HMENU hMenu, UINT id)
{
switch (id) {
case ID_STOPSCRIPT: // 中断脚本
// 中断脚本的选项只在脚本进行中有效
EnableMenuItem(hMenu, id, IsStopScript()? MF_ENABLED: (MF_DISABLED | MF_GRAYED));
break;
}
}
//
// 设定动作
//
BOOL CMainWin::SetAction(int action, int param)
{
switch (action) {
case ActionNop: // 什么都不做
Action = &NopAction;
NopAction.Initialize(this);
break;
case ActionScriptDone: // 脚本结束
StartMainMenu();
break;
case ActionScript: // 执行脚本
Action = &ScriptAction;
break;
}
PostMessage(WM_KICKIDLE); // 为求慎重,传递空消息
return TRUE;
}
//
// 执行脚本
//
bool CMainWin::StartScript(const char *name, int mode)
{
ScriptAction.Initialize(this, mode); // 初始化
if (!ScriptAction.Load(name)) // 读取脚本
return false;
SetAction(ActionScript); // 开始执行
return true;
}
//
// 主菜单
//
// 显示主菜单
//
void CMainWin::StartMainMenu()
{
if (!StartScript("main", MODE_SYSTEM))
::DestroyWindow(hWnd);
}
//
// 显示消息
//
void CMainWin::WriteMessage(const char *msg)
{
FormatMessage(msg);
WaitMarkShowing = TRUE;
ShowMessageWindow();
}
//
// 清除等待输入符号
//
void CMainWin::HideWaitMark()
{
if (WaitMarkShowing) {
WaitMarkShowing = FALSE;
if (TextShow) {
Mixing(WaitMarkRect, TextWaitMark);
Repaint(WaitMarkRect);
}
}
}
//
// 显示菜单
//
void CMainWin::OpenMenu()
{
int maxlen = MENU_MIN_WIDTH;
{
CMemoryDC memdc(0);
HFONT oldFont = memdc.SelectObject(hFont);
for (int i=0; i<MenuCount; i++) {
CSize size;
memdc.GetTextExtentPoint32(MenuBuffer[i].text, MenuBuffer[i].length, &size);
if (maxlen < size.cx)
maxlen = size.cx;
}
memdc.SelectObject(oldFont);
}
MenuRect.top = MENU_Y - ((MENU_FRAME_HEIGHT * 2)
+ MenuCount * MENU_ITEM_HEIGHT - MENU_ITEM_SPACE);
MenuRect.left = MENU_X;
MenuRect.bottom = MENU_Y;
MenuRect.right = MENU_X + (MENU_FRAME_WIDTH * 2) + maxlen;
MenuShow = TRUE;
Mixing(MenuRect);
Repaint(MenuRect);
}
//
// 切换菜单是否显示
//
void CMainWin::SelectMenu(int index, bool select)
{
if (index >= 0) {
MenuBuffer[index].color = select? RedPixel: WhitePixel;
CRect rect;
rect.left = MenuRect.left + MENU_FRAME_WIDTH;
rect.top = MenuRect.top + MENU_FRAME_HEIGHT + MENU_ITEM_HEIGHT * index;
rect.right = rect.left + MenuRect.Width() - MENU_FRAME_WIDTH * 2;
rect.bottom = rect.top + MessageFont;
Mixing(rect, MenuItem(index));
Repaint(rect);
}
}
//
// 显示消息区域
//
void CMainWin::ShowMessageWindow()
{
TextDisplay = TRUE;
TextShow = TRUE;
Invalidate(TextRect);
Update();
}
//
// 清除画面上的消息
//
void CMainWin::HideMessageWindow(bool update)
{
TextDisplay = FALSE;
if (TextShow) {
TextShow = FALSE;
Invalidate(TextRect);
if (update)
Update();
}
}
//
// 切换消息窗口是否显示
//
void CMainWin::FlipMessageWindow()
{
if (TextDisplay) {
TextShow = TextShow? FALSE: TRUE;
Invalidate(TextRect);
Update();
}
}
//
// 显示重叠的CG
//
void CMainWin::ShowOverlapLayer(int pos)
{
if (OverlapShow) { // 是要显示的状态吗?
// 如果是显示在中间时,就删除所有之前显示的图形
if ((OverlapFlags == Center && pos != Center) // 中间 -> 其他
|| (OverlapFlags != Center && pos == Center)) { // 其他 -> 中间
Invalidate(Position[OverlapFlags]);
OverlapFlags = None;
OverlapBounds.SetRectEmpty();
}
}
OverlapFlags |= pos;
OverlapBounds = Position[OverlapFlags];
OverlapShow = TRUE;
Invalidate(Position[pos]);
}
//
// 重叠(overlap)的消去
//
// 实际上只有显示状况变更
//
void CMainWin::HideOverlapLayer(int pos)
{
if (OverlapShow) { // 是要显示的状态吗?
// 如果是显示在中间时,就删除所有之前显示的图形
if ((OverlapFlags == Center && pos != Center) // 中间 -> 其他
|| (OverlapFlags != Center && pos == Center)) { // 其他 -> 中间
Invalidate(Position[OverlapFlags]);
OverlapFlags = None;
OverlapBounds.SetRectEmpty();
}
}
OverlapFlags &= ~pos;
OverlapBounds = Position[OverlapFlags];
if (OverlapFlags == None)
OverlapShow = FALSE;
Invalidate(Position[pos]);
}
//
// 隐藏功能表菜单
//
void CMainWin::HideMenuWindow(bool update)
{
if (MenuShow) {
MenuShow = FALSE;
Invalidate(MenuRect);
if (update)
Update();
}
}
//
// 清除所有图片
//
// pix所指定的就是填满用的色彩
//
void CMainWin::HideAllLayer(COLORREF pix)
{
BgColor = pix;
BackShow = FALSE;
OverlapShow = FALSE;
OverlapFlags = None;
OverlapBounds.SetRectEmpty();
}
//
// CG与文字的合成
//
void CMainWin::Mixing(const CRect &rect, unsigned flags)
{
// 背景
if (BackShow)
MixedImage.Copy(&BackLayer, rect); // 如果有背景就复制
else
MixedImage.FillRect(rect, BgColor); // 如果没有背景就涂满
// 重叠
if (OverlapShow) // 如要要重叠就叠合
MixedImage.MixImage(&OverlapLayer, OverlapBounds & rect);
// 消息区域
if (TextShow) {
if (flags & TextMessage) {
MixedImage.DrawFrameRect(TextRect);
for (int i=0; i<MessageLine; i++) {
MixedImage.DrawText(hFont, MsgX(0), MsgY(i), MsgBuffer[i]);
}
}
else {
MixedImage.FillHalfToneRect(TextRect & rect);
}
if (WaitMarkShowing && flags & TextWaitMark)
MixedImage.DrawText(hFont, MsgX(WAITMARK_X), MsgY(WAITMARK_Y), "▼");
}
// 菜单
if (MenuShow) {
if (flags & MenuFrame)
MixedImage.DrawFrameRect(MenuRect);
else
MixedImage.FillHalfToneRect(MenuRect & rect);
for (int i=0; i<MenuCount; i++) {
if (flags & MenuItem(i)) {
MixedImage.DrawText(hFont,
MenuRect.left + MENU_FRAME_WIDTH,
MenuRect.top + MENU_FRAME_HEIGHT + MENU_ITEM_HEIGHT * i,
MenuBuffer[i].text, MenuBuffer[i].color);
}
}
}
}
//
// 当画面显示变更时,就再描绘
//
BOOL CMainWin::Update(bool repaint)
{
if (!InvalidRect.IsRectEmpty()) { // 有无效区域
Mixing(InvalidRect); // 合成
if (repaint)
Repaint(InvalidRect); // 重绘
InvalidRect.SetRectEmpty(); // 将无效区域设定为“无”
return TRUE; // Update了
}
return FALSE; // 什么都没作
}
//
// 读取背景CG
//
BOOL CMainWin::LoadImageBack(const char *name)
{
BackShow = TRUE;
Invalidate(Position[Both]);
return BackLayer.LoadImage(name);
}
//
// 重叠背景CG
//
BOOL CMainWin::LoadImageOverlap(const char *name, int pos)
{
ShowOverlapLayer(pos);
return OverlapLayer.LoadImage(name, Position[pos].left, Position[pos].top);
}
//
// 删除背景CG
//
BOOL CMainWin::ClearImageBack()
{
BackShow = FALSE;
Invalidate(Position[Both]);
return TRUE;
}
//
// 不合法文字的判断
//
// 换行时要判定行首是否为不合法的文字
// 此字码为检查日文系统的日文字码,不影响中文的处理
// 读者也可依需求加入中文特殊字码的检查
//
BOOL CMainWin::Kinsoku(const char *p)
{
switch (UC(p[0])) {
case 0x81:
switch (UC(p[1])) {
case 0x41:
case 0x42:
case 0x49:
case 0x48:
case 0x5B:
case 0x6A:
case 0x76:
case 0x78:
case 0x99:
case 0xf4:
return TRUE;
}
break;
case 0x82:
switch (UC(p[1])) {
case 0x9f:
case 0xa1:
case 0xa3:
case 0xa5:
case 0xa7:
case 0xe1:
case 0xe3:
case 0xe5:
case 0xc1:
return TRUE;
}
break;
case 0x83:
switch (UC(p[1])) {
case 0x40:
case 0x42:
case 0x44:
case 0x46:
case 0x48:
case 0x83:
case 0x85:
case 0x87:
case 0x62:
return TRUE;
}
}
return FALSE;
}
#define STR_LIMIT (MessageWidth - 2)
#define STR_WIDTH (STR_LIMIT - 2)
//
// 清除消息
//
void CMainWin::ClearMessage()
{
HideMessageWindow();
CurX = CurY = 0;
for (int i=0; i<MessageLine; i++)
MsgBuffer[i][0] = 0;
}
//
// 将消息填入消息缓冲区(MsgBuffer)
//
int CMainWin::FormatMessage(const char *msg)
{
CurX = CurY = 0;
for (int i=0; i<MessageLine; i++)
MsgBuffer[i][0] = 0;
while (*msg && CurY < MessageLine) {
if (*msg == '\n') {
msg++;
MsgBuffer[CurY][CurX] = 0;
CurX = 0;
CurY++;
}
else if (_ismbblead(*msg)) {
if (CurX >= STR_LIMIT || (CurX >= STR_WIDTH && Kinsoku(msg) == 0)) {
MsgBuffer[CurY][CurX] = 0;
CurX = 0;
CurY++;
}
MsgBuffer[CurY][CurX++] = *msg++;
MsgBuffer[CurY][CurX++] = *msg++;
}
else {
if (CurX >= STR_WIDTH) {
MsgBuffer[CurY][CurX] = 0;
CurX = 0;
CurY++;
}
MsgBuffer[CurY][CurX++] = *msg++;
}
}
if (CurX > 0 && CurY < MessageLine) {
MsgBuffer[CurY][CurX] = 0;
CurY++;
}
return CurY;
}
//
// 设定选项到菜单缓冲区
//
void CMainWin::SetMenuItem(const char *str, int anser)
{
int n = strlen(str);
memcpy(MenuBuffer[MenuCount].text, str, n + 1);
MenuBuffer[MenuCount].anser = anser;
MenuBuffer[MenuCount].length = n;
MenuBuffer[MenuCount].color = WhitePixel;
MenuCount++;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -