/* * sprite_event.c: SACT内の mouse/key イベントのハンドラ * * Copyright (C) 1997-1998 Masaki Chikama (Wren) * 1998- * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* $Id: sprite_event.c,v 1.5 2003/11/09 15:06:13 chikama Exp $ */ #include "config.h" #include #ifdef __EMSCRIPTEN__ #include #endif #include "portab.h" #include "system.h" #include "menu.h" #include "input.h" #include "nact.h" #include "sact.h" #include "sprite.h" #include "sactsound.h" #include "sactlog.h" /* SACT内のSpriteKey待ちやメッセージKey待ちなどを実現するため、X|SDL から Key/Mouse イベントがあったときに、nact->ags.eventcb()によって他のモジュール にイベントを送出できるようにした。 SACTでは最初に spev_callback でイベントを受け取るようにし、現在のキー待ちの 状態(sact.waittype)によってそれぞれのイベントハンドラを呼び出している。 現在のところ、1) メッセージキー待ち、2) 単純キー待ち、3) メニューキー待ち、 4) スプライトキー 5) バックログキー待ちの5つがある。 1)メッセージキー待ち 何かキーが押されるまで待つ。Zキースプライトの消去ができる。 2)単純キー待ち 1)と同じ 3)メニューキー待ち メニューオープン時の処理。マウスが動いた時と、キー(orボタン)が離れたときに いくつかの処理を行う。 4)スプライト待ち SW/GETA/GETB/PUT/SWPUTスプライトの各処理を行う。 スプライトは番号の小さいものから順に上に重ねて表示し、下になっている スプライトにイベントが伝わらないようにする -> SpriteKey待ちの直前にスプライトの番号による depth map を作成し、 マウスの位置にあるスプライトの番号をで取り出す。 AlphaマスクつきのSpriteの場合、それを反映し、表示している場所のみに depthmapを反映。 drag中のスプライトは常に最上位にくるように表示し、マウス移動等のイベント は最初に処理する。 5)バックログ表示中のキー待ち オリジナルと同じキー操作のはず */ static void cb_focused_swsp(void* s, void* data); static void cb_defocused_swsp(void* s, void* data); static int cb_focused(sprite_t *sp); static int cb_defocused(sprite_t *sp); static void cb_waitkey_simple(agsevent_t *e); static void cb_waitkey_selection(agsevent_t *e); /* フォーカスを得たスプライトに説明スプライトが登録されていた場合の 説明スプライトの表示ON */ static void cb_focused_swsp(void* s, void* data) { sprite_t *sp = (sprite_t *)s; int *update = (int *)data; bool oldstate = sp->show; SACT_DEBUG("show up spex %d", sp->no); sp->show = true; if (oldstate != sp->show) { (*update)++; sp_updateme(sp); } } /* フォーカスを得たスプライトに説明スプライトが登録されていた場合の 説明スプライトの表示OFF */ static void cb_defocused_swsp(void* s, void* data) { sprite_t *sp = (sprite_t *)s; int *update = (int *)data; bool oldstate = sp->show; SACT_DEBUG("hide spex %d", sp->no); sp->show = false; if (oldstate != sp->show) { (*update)++; sp_updateme(sp); } } // zkey hide off static void cb_focused_zkey(void* s, void* data) { sprite_t *sp = (sprite_t *)s; int *update = (int *)data; bool oldstate = sp->show; sp->show = sp->show_save; if (oldstate != sp->show) { (*update)++; sp_updateme(sp); } } // zkey hide on static void cb_defocused_zkey(void* s, void* data) { sprite_t *sp = (sprite_t *)s; int *update = (int *)data; bool oldstate = sp->show; sp->show = false; if (oldstate != sp->show) { (*update)++; sp_updateme(sp); sp->show_save = oldstate; } } /* フォーカスを得たスプライトの処理 cg2があればcurcgをcg2に設定 drag中のスプライトがある場合 -> PUT/SWPUTスプライトのみ反応 drag中のスプライトがない場合 -> GETA/GETB/SWPUTスプライトのみ反応 */ static int cb_focused(sprite_t *sp) { int update = 0; if (sact.draggedsp) { if (sp->type != SPRITE_PUT && sp->type != SPRITE_SWPUT) return 0; } else { if (sp->type == SPRITE_PUT) return 0; } if (!sp->focused) { if (sp->cg2) { if (sp->curcg != sp->cg2) { sp_updateme(sp); } sp->curcg = sp->cg2; update++; } sp->focused = true; SACT_DEBUG("get forcused %d, type %d", sp->no, sp->type); if (sp->numsound1) { ssnd_play(sp->numsound1); } } return update; } /* フォーカスを失ったスプライトの処理 curcg を cg1 にセット */ static int cb_defocused(sprite_t *sp) { int update = 0; if (sp->focused) { if (sp->curcg != sp->cg1) { sp_updateme(sp); } sp->curcg = sp->cg1; update++; sp->focused = false; SACT_DEBUG("lost forcused %d", sp->no); } return update; } /* WaitKeySimpleのcallback */ static void cb_waitkey_simple(agsevent_t *e) { int cur, update = 0; switch (e->type) { case AGSEVENT_KEY_PRESS: if (e->code == KEY_Z) { cur = sys_get_ticks(); if (!sact.zhiding) { slist_foreach(sact.sp_zhide, cb_defocused_zkey, &update); sact.zhiding = true; sact.zdooff = true; sact.zofftime = cur; } else { sact.zdooff = false; } } break; case AGSEVENT_MOUSE_WHEEL: // back log view mode に移行 // MessageKey 待ちのときのみ if (sact.waittype != KEYWAIT_MESSAGE) break; if (sblog_start()) sact.waittype = KEYWAIT_BACKLOG; break; case AGSEVENT_BUTTON_RELEASE: if (sact.zhiding) { slist_foreach(sact.sp_zhide, cb_focused_zkey, &update); sact.zhiding = false; } // fall through case AGSEVENT_KEY_RELEASE: switch(e->code) { case KEY_Z: cur = sys_get_ticks(); if (500 < (cur - sact.zofftime) || !sact.zdooff) { slist_foreach(sact.sp_zhide, cb_focused_zkey, &update); sact.zhiding = false; } break; case KEY_PAGEUP: case KEY_PAGEDOWN: // MessageKey 待ちのときのみ if (sact.waittype != KEYWAIT_MESSAGE) break; if (sblog_start()) sact.waittype = KEYWAIT_BACKLOG; break; default: sact.waitkey = e->code; break; } } if (update) { sp_update_clipped(); } } /* WaitKeySpriteのcallback */ EMSCRIPTEN_KEEPALIVE void cb_waitkey_sprite(agsevent_t *e) { SList *node; sprite_t *focused_sp = NULL; // focus を得ている sprite sprite_t *defocused_sp = NULL; // focus を失った sprite int update = 0; // キーイベントは無視 switch(e->type) { case AGSEVENT_KEY_RELEASE: case AGSEVENT_KEY_PRESS: return; } if (sact.draggedsp) { // 先に drag中のspriteにイベントを送る update = sact.draggedsp->eventcb(sact.draggedsp, e); // Async in emscripten } else { // 右クリックキャンセル // drag中でない時のみ、キャンセルを受け付ける if (e->type == AGSEVENT_BUTTON_RELEASE && e->code == AGSEVENT_BUTTON_RIGHT) { sact.waitkey = 0; return; } } // forcusを得ている sprite と focusを失った sprite を探す for (node = sact.eventlisteners; node; node = node->next) { sprite_t *sp = (sprite_t *)node->data; if (sp == NULL) continue; if (!sp->show) continue; // freeze状態ではCGは変化しない if (sp->freezed_state != 0) continue; // dragg中の sprite は無視する if (sp == sact.draggedsp) continue; if (focused_sp == NULL && sp_is_insprite(sp, e->mousex, e->mousey)) { /* focusを得ている sprite */ update += cb_focused(sp); focused_sp = sp; } else { /* 現在のカーソル位置にはいま処理している番号のsprite は存在しないので、defocus の処理を行う。 今回のマウス移動イベントによりfocusを失ったので あれば、cb_decocused(sp)は 1 を返す */ int ret = cb_defocused(sp); if (ret > 0) defocused_sp = sp; update += ret; } } // focusを得た sprite に BUTTONイベントのみを送る if (focused_sp && e->type != AGSEVENT_MOUSE_MOTION) { update += focused_sp->eventcb(focused_sp, e); } // 範囲外(focusを得ているspriteがない場合)をクリックしたときの音 if (!focused_sp && e->type != AGSEVENT_MOUSE_MOTION && sact.numsoundob) { ssnd_play(sact.numsoundob); } // drag中でない場合は、説明スプライトの表示、消去を行う if (sact.draggedsp == NULL && e->type == AGSEVENT_MOUSE_MOTION) { // focus を失った sprite の 説明 sprite の消去 if (defocused_sp) { sprite_t *sp = defocused_sp; if (sp->expsp) { slist_foreach(sp->expsp, cb_defocused_swsp, &update); } } // focus を得た sprite の 説明 sprite の表示 if (focused_sp) { sprite_t *sp = focused_sp; if (sp->expsp) { slist_foreach(sp->expsp, cb_focused_swsp, &update); } } } // 表示状態に変更があればその領域を更新 if (update) { sp_update_clipped(); } } /* 選択肢 Window Open 時の callback */ static void cb_waitkey_selection(agsevent_t *e) { switch (e->type) { case AGSEVENT_BUTTON_RELEASE: sact.sel.cbrelease(e); break; case AGSEVENT_MOUSE_MOTION: sact.sel.cbmove(e); break; } } /* バックログ参照時 */ static void cb_waitkey_backlog(agsevent_t *e) { switch (e->type) { case AGSEVENT_KEY_RELEASE: switch (e->code) { case KEY_ESCAPE: sblog_end(); sact.waittype = KEYWAIT_MESSAGE; break; case KEY_PAGEUP: sblog_pageup(); break; case KEY_PAGEDOWN: sblog_pagedown(); break; case KEY_UP: sblog_pagenext(); break; case KEY_DOWN: sblog_pagepre(); break; } break; case AGSEVENT_BUTTON_RELEASE: if (e->code == AGSEVENT_BUTTON_RIGHT) { sblog_end(); sact.waittype = KEYWAIT_MESSAGE; } break; case AGSEVENT_MOUSE_WHEEL: if (e->code > 0) sblog_pagenext(); else sblog_pagepre(); break; } } /* X|SDL のイベントディスパッチャからくる最初の場所 */ void spev_callback(agsevent_t *e) { // menu open中は無視 if (nact->popupmenu_opened) { return; } if (sact.waittype != KEYWAIT_BACKLOG) { if (e->type == AGSEVENT_KEY_PRESS && e->code == KEY_CTRL) { sact.waitskiplv = 2; sact.waitkey = e->code; return; } if (e->type == AGSEVENT_KEY_RELEASE && e->code == KEY_CTRL) { sact.waitskiplv = 0; sact.waitkey = e->code; return; } } switch (sact.waittype) { case KEYWAIT_MESSAGE: case KEYWAIT_SIMPLE: cb_waitkey_simple(e); break; case KEYWAIT_SPRITE: cb_waitkey_sprite(e); break; case KEYWAIT_SELECT: cb_waitkey_selection(e); break; case KEYWAIT_BACKLOG: cb_waitkey_backlog(e); break; default: return; } } /* 各スプライト毎のイベント callback の登録 */ void spev_add_eventlistener(sprite_t *sp, int (*cb)(sprite_t *, agsevent_t *)) { sp->eventcb = cb; sact.eventlisteners = slist_append(sact.eventlisteners, sp); } /* 上で登録した callback の削除 */ void spev_remove_eventlistener(sprite_t *sp) { sact.eventlisteners = slist_remove(sact.eventlisteners, sp); }