From: Mario Date: Wed, 8 Jul 2015 15:02:49 +0000 (+1000) Subject: Add the new minigames X-Git-Tag: xonotic-v0.8.2~2038^2~6 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=2e4239626dbb271c4bbfe53e0120ffc40e1b7c6c;p=xonotic%2Fxonotic-data.pk3dir.git Add the new minigames --- diff --git a/gfx/hud/default/minigames/c4/board.jpg b/gfx/hud/default/minigames/c4/board.jpg new file mode 100644 index 0000000000..8ab7a8d3be Binary files /dev/null and b/gfx/hud/default/minigames/c4/board.jpg differ diff --git a/gfx/hud/default/minigames/c4/board_alpha.jpg b/gfx/hud/default/minigames/c4/board_alpha.jpg new file mode 100644 index 0000000000..416a8584d8 Binary files /dev/null and b/gfx/hud/default/minigames/c4/board_alpha.jpg differ diff --git a/gfx/hud/default/minigames/c4/board_over.jpg b/gfx/hud/default/minigames/c4/board_over.jpg new file mode 100644 index 0000000000..9025039805 Binary files /dev/null and b/gfx/hud/default/minigames/c4/board_over.jpg differ diff --git a/gfx/hud/default/minigames/c4/board_over_alpha.jpg b/gfx/hud/default/minigames/c4/board_over_alpha.jpg new file mode 100644 index 0000000000..4befedf58e Binary files /dev/null and b/gfx/hud/default/minigames/c4/board_over_alpha.jpg differ diff --git a/gfx/hud/default/minigames/c4/board_under.jpg b/gfx/hud/default/minigames/c4/board_under.jpg new file mode 100644 index 0000000000..3f364b3372 Binary files /dev/null and b/gfx/hud/default/minigames/c4/board_under.jpg differ diff --git a/gfx/hud/default/minigames/c4/board_under_alpha.jpg b/gfx/hud/default/minigames/c4/board_under_alpha.jpg new file mode 100644 index 0000000000..22c9bd124c Binary files /dev/null and b/gfx/hud/default/minigames/c4/board_under_alpha.jpg differ diff --git a/gfx/hud/default/minigames/c4/icon.jpg b/gfx/hud/default/minigames/c4/icon.jpg new file mode 100644 index 0000000000..85cf11f0cb Binary files /dev/null and b/gfx/hud/default/minigames/c4/icon.jpg differ diff --git a/gfx/hud/default/minigames/c4/icon_notif.jpg b/gfx/hud/default/minigames/c4/icon_notif.jpg new file mode 100644 index 0000000000..f2804506ca Binary files /dev/null and b/gfx/hud/default/minigames/c4/icon_notif.jpg differ diff --git a/gfx/hud/default/minigames/c4/icon_notif_alpha.jpg b/gfx/hud/default/minigames/c4/icon_notif_alpha.jpg new file mode 100644 index 0000000000..040990f6d5 Binary files /dev/null and b/gfx/hud/default/minigames/c4/icon_notif_alpha.jpg differ diff --git a/gfx/hud/default/minigames/c4/piece1.jpg b/gfx/hud/default/minigames/c4/piece1.jpg new file mode 100644 index 0000000000..00a869be4a Binary files /dev/null and b/gfx/hud/default/minigames/c4/piece1.jpg differ diff --git a/gfx/hud/default/minigames/c4/piece1_alpha.jpg b/gfx/hud/default/minigames/c4/piece1_alpha.jpg new file mode 100644 index 0000000000..6b45fa1f84 Binary files /dev/null and b/gfx/hud/default/minigames/c4/piece1_alpha.jpg differ diff --git a/gfx/hud/default/minigames/c4/piece2.jpg b/gfx/hud/default/minigames/c4/piece2.jpg new file mode 100644 index 0000000000..e1f14a57f5 Binary files /dev/null and b/gfx/hud/default/minigames/c4/piece2.jpg differ diff --git a/gfx/hud/default/minigames/c4/piece2_alpha.jpg b/gfx/hud/default/minigames/c4/piece2_alpha.jpg new file mode 100644 index 0000000000..6b45fa1f84 Binary files /dev/null and b/gfx/hud/default/minigames/c4/piece2_alpha.jpg differ diff --git a/gfx/hud/default/minigames/c4/winglow.jpg b/gfx/hud/default/minigames/c4/winglow.jpg new file mode 100644 index 0000000000..bc21db0fbe Binary files /dev/null and b/gfx/hud/default/minigames/c4/winglow.jpg differ diff --git a/gfx/hud/default/minigames/c4/winglow_alpha.jpg b/gfx/hud/default/minigames/c4/winglow_alpha.jpg new file mode 100644 index 0000000000..02c5ff8564 Binary files /dev/null and b/gfx/hud/default/minigames/c4/winglow_alpha.jpg differ diff --git a/gfx/hud/default/minigames/pp/board.jpg b/gfx/hud/default/minigames/pp/board.jpg new file mode 100644 index 0000000000..b435e3da99 Binary files /dev/null and b/gfx/hud/default/minigames/pp/board.jpg differ diff --git a/gfx/hud/default/minigames/pp/board_alpha.jpg b/gfx/hud/default/minigames/pp/board_alpha.jpg new file mode 100644 index 0000000000..14938ed3a4 Binary files /dev/null and b/gfx/hud/default/minigames/pp/board_alpha.jpg differ diff --git a/gfx/hud/default/minigames/pp/icon.jpg b/gfx/hud/default/minigames/pp/icon.jpg new file mode 100644 index 0000000000..7889f1bd89 Binary files /dev/null and b/gfx/hud/default/minigames/pp/icon.jpg differ diff --git a/gfx/hud/default/minigames/pp/icon_notif.jpg b/gfx/hud/default/minigames/pp/icon_notif.jpg new file mode 100644 index 0000000000..b036d95d43 Binary files /dev/null and b/gfx/hud/default/minigames/pp/icon_notif.jpg differ diff --git a/gfx/hud/default/minigames/pp/icon_notif_alpha.jpg b/gfx/hud/default/minigames/pp/icon_notif_alpha.jpg new file mode 100644 index 0000000000..040990f6d5 Binary files /dev/null and b/gfx/hud/default/minigames/pp/icon_notif_alpha.jpg differ diff --git a/gfx/hud/default/minigames/pp/piece1.jpg b/gfx/hud/default/minigames/pp/piece1.jpg new file mode 100644 index 0000000000..9bb789d78b Binary files /dev/null and b/gfx/hud/default/minigames/pp/piece1.jpg differ diff --git a/gfx/hud/default/minigames/pp/piece1_alpha.jpg b/gfx/hud/default/minigames/pp/piece1_alpha.jpg new file mode 100644 index 0000000000..9bb789d78b Binary files /dev/null and b/gfx/hud/default/minigames/pp/piece1_alpha.jpg differ diff --git a/gfx/hud/default/minigames/pp/piece2.jpg b/gfx/hud/default/minigames/pp/piece2.jpg new file mode 100644 index 0000000000..7701f42a33 Binary files /dev/null and b/gfx/hud/default/minigames/pp/piece2.jpg differ diff --git a/gfx/hud/default/minigames/pp/piece2_alpha.jpg b/gfx/hud/default/minigames/pp/piece2_alpha.jpg new file mode 100644 index 0000000000..7701f42a33 Binary files /dev/null and b/gfx/hud/default/minigames/pp/piece2_alpha.jpg differ diff --git a/gfx/hud/default/minigames/pp/piece_current.jpg b/gfx/hud/default/minigames/pp/piece_current.jpg new file mode 100644 index 0000000000..02afe48275 Binary files /dev/null and b/gfx/hud/default/minigames/pp/piece_current.jpg differ diff --git a/gfx/hud/default/minigames/pp/piece_current_alpha.jpg b/gfx/hud/default/minigames/pp/piece_current_alpha.jpg new file mode 100644 index 0000000000..02afe48275 Binary files /dev/null and b/gfx/hud/default/minigames/pp/piece_current_alpha.jpg differ diff --git a/gfx/hud/default/minigames/pp/piece_selected.jpg b/gfx/hud/default/minigames/pp/piece_selected.jpg new file mode 100644 index 0000000000..eaf997095d Binary files /dev/null and b/gfx/hud/default/minigames/pp/piece_selected.jpg differ diff --git a/gfx/hud/default/minigames/pp/piece_selected_alpha.jpg b/gfx/hud/default/minigames/pp/piece_selected_alpha.jpg new file mode 100644 index 0000000000..71d65fd8ae Binary files /dev/null and b/gfx/hud/default/minigames/pp/piece_selected_alpha.jpg differ diff --git a/gfx/hud/default/minigames/pp/piece_taken.jpg b/gfx/hud/default/minigames/pp/piece_taken.jpg new file mode 100644 index 0000000000..cafa6c3cb8 Binary files /dev/null and b/gfx/hud/default/minigames/pp/piece_taken.jpg differ diff --git a/gfx/hud/default/minigames/pp/piece_taken_alpha.jpg b/gfx/hud/default/minigames/pp/piece_taken_alpha.jpg new file mode 100644 index 0000000000..cafa6c3cb8 Binary files /dev/null and b/gfx/hud/default/minigames/pp/piece_taken_alpha.jpg differ diff --git a/gfx/hud/default/minigames/ps/board.jpg b/gfx/hud/default/minigames/ps/board.jpg new file mode 100644 index 0000000000..882e4d2184 Binary files /dev/null and b/gfx/hud/default/minigames/ps/board.jpg differ diff --git a/gfx/hud/default/minigames/ps/board_alpha.jpg b/gfx/hud/default/minigames/ps/board_alpha.jpg new file mode 100644 index 0000000000..43f18b4ed1 Binary files /dev/null and b/gfx/hud/default/minigames/ps/board_alpha.jpg differ diff --git a/gfx/hud/default/minigames/ps/icon.jpg b/gfx/hud/default/minigames/ps/icon.jpg new file mode 100644 index 0000000000..1390c895e3 Binary files /dev/null and b/gfx/hud/default/minigames/ps/icon.jpg differ diff --git a/gfx/hud/default/minigames/ps/icon_notif.jpg b/gfx/hud/default/minigames/ps/icon_notif.jpg new file mode 100644 index 0000000000..b9024ae04c Binary files /dev/null and b/gfx/hud/default/minigames/ps/icon_notif.jpg differ diff --git a/gfx/hud/default/minigames/ps/icon_notif_alpha.jpg b/gfx/hud/default/minigames/ps/icon_notif_alpha.jpg new file mode 100644 index 0000000000..040990f6d5 Binary files /dev/null and b/gfx/hud/default/minigames/ps/icon_notif_alpha.jpg differ diff --git a/gfx/hud/default/minigames/ps/piece.jpg b/gfx/hud/default/minigames/ps/piece.jpg new file mode 100644 index 0000000000..210389da4e Binary files /dev/null and b/gfx/hud/default/minigames/ps/piece.jpg differ diff --git a/gfx/hud/default/minigames/ps/piece_alpha.jpg b/gfx/hud/default/minigames/ps/piece_alpha.jpg new file mode 100644 index 0000000000..6b45fa1f84 Binary files /dev/null and b/gfx/hud/default/minigames/ps/piece_alpha.jpg differ diff --git a/gfx/hud/default/minigames/ps/tile_available.jpg b/gfx/hud/default/minigames/ps/tile_available.jpg new file mode 100644 index 0000000000..83551d7c6f Binary files /dev/null and b/gfx/hud/default/minigames/ps/tile_available.jpg differ diff --git a/gfx/hud/default/minigames/ps/tile_available_alpha.jpg b/gfx/hud/default/minigames/ps/tile_available_alpha.jpg new file mode 100644 index 0000000000..e265d34a44 Binary files /dev/null and b/gfx/hud/default/minigames/ps/tile_available_alpha.jpg differ diff --git a/gfx/hud/default/minigames/ps/tile_selected.jpg b/gfx/hud/default/minigames/ps/tile_selected.jpg new file mode 100644 index 0000000000..797aee9e22 Binary files /dev/null and b/gfx/hud/default/minigames/ps/tile_selected.jpg differ diff --git a/gfx/hud/default/minigames/ps/tile_selected_alpha.jpg b/gfx/hud/default/minigames/ps/tile_selected_alpha.jpg new file mode 100644 index 0000000000..e265d34a44 Binary files /dev/null and b/gfx/hud/default/minigames/ps/tile_selected_alpha.jpg differ diff --git a/gfx/hud/default/minigames/qto/board.jpg b/gfx/hud/default/minigames/qto/board.jpg new file mode 100644 index 0000000000..cf8fe0a778 Binary files /dev/null and b/gfx/hud/default/minigames/qto/board.jpg differ diff --git a/gfx/hud/default/minigames/qto/board_alpha.jpg b/gfx/hud/default/minigames/qto/board_alpha.jpg new file mode 100644 index 0000000000..44a87081f0 Binary files /dev/null and b/gfx/hud/default/minigames/qto/board_alpha.jpg differ diff --git a/gfx/hud/default/minigames/qto/icon.jpg b/gfx/hud/default/minigames/qto/icon.jpg new file mode 100644 index 0000000000..a534f488cf Binary files /dev/null and b/gfx/hud/default/minigames/qto/icon.jpg differ diff --git a/gfx/hud/default/minigames/qto/icon_notif.jpg b/gfx/hud/default/minigames/qto/icon_notif.jpg new file mode 100644 index 0000000000..90e0e2ee75 Binary files /dev/null and b/gfx/hud/default/minigames/qto/icon_notif.jpg differ diff --git a/gfx/hud/default/minigames/qto/icon_notif_alpha.jpg b/gfx/hud/default/minigames/qto/icon_notif_alpha.jpg new file mode 100644 index 0000000000..040990f6d5 Binary files /dev/null and b/gfx/hud/default/minigames/qto/icon_notif_alpha.jpg differ diff --git a/gfx/hud/default/minigames/qto/piece0.tga b/gfx/hud/default/minigames/qto/piece0.tga new file mode 100644 index 0000000000..fad7dbdb29 Binary files /dev/null and b/gfx/hud/default/minigames/qto/piece0.tga differ diff --git a/gfx/hud/default/minigames/qto/piece1.tga b/gfx/hud/default/minigames/qto/piece1.tga new file mode 100644 index 0000000000..80a34c21d9 Binary files /dev/null and b/gfx/hud/default/minigames/qto/piece1.tga differ diff --git a/qcsrc/common/minigames/cl_minigames.qc b/qcsrc/common/minigames/cl_minigames.qc index 0bac54c678..e8eee26245 100644 --- a/qcsrc/common/minigames/cl_minigames.qc +++ b/qcsrc/common/minigames/cl_minigames.qc @@ -105,6 +105,7 @@ void deactivate_minigame() { if ( !active_minigame ) return; + active_minigame.minigame_event(active_minigame,"deactivate"); entity e = world; while( (e = findentity(e, owner, active_minigame)) ) @@ -255,7 +256,7 @@ void ent_read_minigame() if ( minigame_ent ) minigame_ent.minigame_event(minigame_ent,"network_receive",self,sf); - + if ( sf & MINIG_SF_CREATE ) { dprint("CL Reading entity: ",ftos(num_for_edict(self)), @@ -287,7 +288,7 @@ string minigame_getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_ if ( take_until > strlen(s) ) take_until = strlen(s); - + for ( int i = 0; i < take_until; i++ ) if ( substring(s,i,1) == "\n" ) { @@ -404,4 +405,4 @@ void minigame_prompt() HUD_Notify_Push(sprintf("minigames/%s/icon_notif",active_minigame.descriptor.netname), _("It's your turn"), ""); } -} \ No newline at end of file +} diff --git a/qcsrc/common/minigames/cl_minigames_hud.qc b/qcsrc/common/minigames/cl_minigames_hud.qc index 4850224d95..b7b7df128a 100644 --- a/qcsrc/common/minigames/cl_minigames_hud.qc +++ b/qcsrc/common/minigames/cl_minigames_hud.qc @@ -79,22 +79,22 @@ entity HUD_MinigameMenu_entries; entity HUD_MinigameMenu_last_entry; // Minigame menu options: insert entry after the given location -void HUD_MinigameMenu_InsertEntry(entity entry, entity prev) +void HUD_MinigameMenu_InsertEntry(entity newentry, entity prev) { if ( !HUD_MinigameMenu_entries ) { - HUD_MinigameMenu_entries = entry; - HUD_MinigameMenu_last_entry = entry; + HUD_MinigameMenu_entries = newentry; + HUD_MinigameMenu_last_entry = newentry; return; } - entry.list_prev = prev; - entry.list_next = prev.list_next; + newentry.list_prev = prev; + newentry.list_next = prev.list_next; if ( prev.list_next ) - prev.list_next.list_prev = entry; + prev.list_next.list_prev = newentry; else - HUD_MinigameMenu_last_entry = entry; - prev.list_next = entry; + HUD_MinigameMenu_last_entry = newentry; + prev.list_next = newentry; } @@ -484,7 +484,7 @@ void HUD_MinigameMenu () HUD_MinigameMenu_DrawEntry(panel_pos,_("Minigames"),hud_fontsize*2,'0.25 0.47 0.72'); panel_pos_y += hud_fontsize_y*2; - + vector color; vector offset; float itemh; diff --git a/qcsrc/common/minigames/minigame/all.qh b/qcsrc/common/minigames/minigame/all.qh index a63d3335fe..d1899e7594 100644 --- a/qcsrc/common/minigames/minigame/all.qh +++ b/qcsrc/common/minigames/minigame/all.qh @@ -63,7 +63,11 @@ that .owner is set to the minigame session entity and .minigame_autoclean is tru #include "nmm.qc" #include "ttt.qc" +#include "c4.qc" #include "pong.qc" +#include "qto.qc" +#include "ps.qc" +#include "pp.qc" /** * Registration: @@ -74,7 +78,11 @@ that .owner is set to the minigame session entity and .minigame_autoclean is tru #define REGISTERED_MINIGAMES \ MINIGAME(nmm, "Nine Men's Morris") \ MINIGAME(ttt, "Tic Tac Toe") \ - MINIGAME(pong, "Pong") \ + MINIGAME(pong,"Pong") \ + MINIGAME(c4, "Connect Four") \ + MINIGAME(qto, "Quinto") \ + MINIGAME(ps, "Peg Solitaire") \ + MINIGAME(pp, "Push-Pull") \ /*empty line*/ /** diff --git a/qcsrc/common/minigames/minigame/c4.qc b/qcsrc/common/minigames/minigame/c4.qc new file mode 100644 index 0000000000..a153b39939 --- /dev/null +++ b/qcsrc/common/minigames/minigame/c4.qc @@ -0,0 +1,505 @@ +const float C4_TURN_PLACE = 0x0100; // player has to place a piece on the board +const float C4_TURN_WIN = 0x0200; // player has won +const float C4_TURN_DRAW = 0x0400; // no moves are possible +const float C4_TURN_TYPE = 0x0f00; // turn type mask + +const float C4_TURN_TEAM1 = 0x0001; +const float C4_TURN_TEAM2 = 0x0002; +const float C4_TURN_TEAM = 0x000f; // turn team mask + +const int C4_LET_CNT = 7; +const int C4_NUM_CNT = 6; +const int C4_WIN_CNT = 4; + +const int C4_MAX_TILES = 42; + +const int C4_TILE_SIZE = 8; + +const int C4_TEAMS = 2; + +.int c4_npieces; // (minigame) number of pieces on the board (simplifies checking a draw) +.int c4_nexteam; // (minigame) next team (used to change the starting team on following matches) + +// find connect 4 piece given its tile name +entity c4_find_piece(entity minig, string tile) +{ + entity e = world; + while ( ( e = findentity(e,owner,minig) ) ) + if ( e.classname == "minigame_board_piece" && e.netname == tile ) + return e; + return world; +} + +// Checks if the given piece completes a row +bool c4_winning_piece(entity piece) +{ + int number = minigame_tile_number(piece.netname); + int letter = minigame_tile_letter(piece.netname); + + int i; + entity top = piece; + entity left = piece; + entity topleft = piece; + entity botleft = piece; + for(i = number; i < C4_NUM_CNT; ++i) + { + entity p = c4_find_piece(piece.owner,minigame_tile_buildname(letter, i)); + if(p.team == piece.team) + top = p; + else break; + } + + for(i = letter; i >= 0; --i) + { + entity p = c4_find_piece(piece.owner,minigame_tile_buildname(i, number)); + if(p.team == piece.team) + left = p; + else break; + } + + int j; + for(i = letter, j = number; i >= 0, j >= 0; --i, --j) + { + entity p = c4_find_piece(piece.owner,minigame_tile_buildname(i, j)); + if(p.team == piece.team) + botleft = p; + else break; + } + for(i = letter, j = number; i >= 0, j < C4_NUM_CNT; --i, ++j) + { + entity p = c4_find_piece(piece.owner,minigame_tile_buildname(i, j)); + if(p.team == piece.team) + topleft = p; + else break; + } + + // down + int found = 0; + for(i = minigame_tile_number(top.netname); i >= 0; --i) + { + if(c4_find_piece(piece.owner,minigame_tile_buildname(letter, i)).team == piece.team) + ++found; + else break; + } + + if(found >= C4_WIN_CNT) + return true; + + // right + found = 0; + for(i = minigame_tile_letter(left.netname); i < C4_LET_CNT; ++i) + { + if(c4_find_piece(piece.owner,minigame_tile_buildname(i, number)).team == piece.team) + ++found; + else break; + } + + if(found >= C4_WIN_CNT) + return true; + + // diagright down + found = 0; + for(i = minigame_tile_letter(topleft.netname), j = minigame_tile_number(topleft.netname); i < C4_LET_CNT, j >= 0; ++i, --j) + { + if(c4_find_piece(piece.owner,minigame_tile_buildname(i, j)).team == piece.team) + ++found; + else break; + } + + if(found >= C4_WIN_CNT) + return true; + + // diagright up + found = 0; + for(i = minigame_tile_letter(botleft.netname), j = minigame_tile_number(botleft.netname); i < C4_LET_CNT, j < C4_NUM_CNT; ++i, ++j) + { + if(c4_find_piece(piece.owner,minigame_tile_buildname(i, j)).team == piece.team) + ++found; + else break; + } + + if(found >= C4_WIN_CNT) + return true; + + return false; +} + +// check if the tile name is valid (6x7 grid) +bool c4_valid_tile(string tile) +{ + if ( !tile ) + return false; + float number = minigame_tile_number(tile); + float letter = minigame_tile_letter(tile); + return 0 <= number && number < C4_NUM_CNT && 0 <= letter && letter < C4_LET_CNT; +} + +string c4_get_lowest_tile(entity minigame, string s) +{ + int i; + int end = 0; + for(i = C4_NUM_CNT; i >= 0; --i) + { + if(!c4_find_piece(minigame,minigame_tile_buildname(minigame_tile_letter(s), i))) + if(c4_find_piece(minigame,minigame_tile_buildname(minigame_tile_letter(s), i - 1))) + { + end = i; + break; + } + } + return minigame_tile_buildname(minigame_tile_letter(s), end); +} + +// make a move +void c4_move(entity minigame, entity player, string pos ) +{ + pos = c4_get_lowest_tile(minigame, pos); + + if ( minigame.minigame_flags & C4_TURN_PLACE ) + if ( pos && player.team == (minigame.minigame_flags & C4_TURN_TEAM) ) + { + if ( c4_valid_tile(pos) ) + if ( !c4_find_piece(minigame,pos) ) + { + entity piece = msle_spawn(minigame,"minigame_board_piece"); + piece.team = player.team; + piece.netname = strzone(pos); + minigame_server_sendflags(piece,MINIG_SF_ALL); + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + minigame.c4_npieces++; + minigame.c4_nexteam = minigame_next_team(player.team,C4_TEAMS); + if ( c4_winning_piece(piece) ) + { + minigame.minigame_flags = C4_TURN_WIN | player.team; + } + else if ( minigame.c4_npieces >= C4_MAX_TILES ) + minigame.minigame_flags = C4_TURN_DRAW; + else + minigame.minigame_flags = C4_TURN_PLACE | minigame.c4_nexteam; + } + } +} + +#ifdef SVQC + + +// required function, handle server side events +int c4_server_event(entity minigame, string event, ...) +{ + switch(event) + { + case "start": + { + minigame.minigame_flags = (C4_TURN_PLACE | C4_TURN_TEAM1); + return true; + } + case "end": + { + entity e = world; + while( (e = findentity(e, owner, minigame)) ) + if(e.classname == "minigame_board_piece") + { + if(e.netname) { strunzone(e.netname); } + remove(e); + } + return false; + } + case "join": + { + int pl_num = minigame_count_players(minigame); + + // Don't allow more than 2 players + if(pl_num >= C4_TEAMS) { return false; } + + // Get the right team + if(minigame.minigame_players) + return minigame_next_team(minigame.minigame_players.team, C4_TEAMS); + + // Team 1 by default + return 1; + } + case "cmd": + { + switch(argv(0)) + { + case "move": + c4_move(minigame, ...(0,entity), ...(1,int) == 2 ? argv(1) : string_null ); + return true; + } + + return false; + } + } + + return false; +} + + +#elif defined(CSQC) + +string c4_curr_pos; // identifier of the tile under the mouse +vector c4_boardpos; // HUD board position +vector c4_boardsize;// HUD board size +.int c4_checkwin; // Used to optimize checks to display a win + +// Required function, draw the game board +void c4_hud_board(vector pos, vector mySize) +{ + minigame_hud_fitsqare(pos, mySize); + c4_boardpos = pos; + c4_boardsize = mySize; + + minigame_hud_simpleboard(pos,mySize,minigame_texture("c4/board_under")); + + drawpic(pos, minigame_texture("c4/board_over"), mySize, '1 1 1', 1, 0); + + vector tile_size = minigame_hud_denormalize_size('1 1 0' / C4_TILE_SIZE,pos,mySize); + vector tile_pos; + + if ( (active_minigame.minigame_flags & C4_TURN_TEAM) == minigame_self.team ) + if ( c4_valid_tile(c4_curr_pos) ) + { + tile_pos = minigame_tile_pos(c4_curr_pos,C4_NUM_CNT,C4_LET_CNT); + tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); + minigame_drawpic_centered( tile_pos, + minigame_texture(strcat("c4/piece",ftos(minigame_self.team))), + tile_size, '1 1 1', panel_fg_alpha/2, DRAWFLAG_NORMAL ); + } + + entity e; + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_board_piece" ) + { + tile_pos = minigame_tile_pos(e.netname,C4_NUM_CNT,C4_LET_CNT); + tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); + + if ( active_minigame.minigame_flags & C4_TURN_WIN ) + if ( !e.c4_checkwin ) + e.c4_checkwin = c4_winning_piece(e) ? 1 : -1; + + float icon_color = 1; + if ( e.c4_checkwin == -1 ) + icon_color = 0.4; + else if ( e.c4_checkwin == 1 ) + { + icon_color = 2; + minigame_drawpic_centered( tile_pos, minigame_texture("c4/winglow"), + tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_ADDITIVE ); + } + + minigame_drawpic_centered( tile_pos, + minigame_texture(strcat("c4/piece",ftos(e.team))), + tile_size, '1 1 1'*icon_color, panel_fg_alpha, DRAWFLAG_NORMAL ); + } + } + + if ( active_minigame.minigame_flags & C4_TURN_WIN ) + { + vector winfs = hud_fontsize*2; + string playername = ""; + FOREACH_MINIGAME_ENTITY(e) + if ( e.classname == "minigame_player" && + e.team == (active_minigame.minigame_flags & C4_TURN_TEAM) ) + playername = GetPlayerName(e.minigame_playerslot-1); + + vector win_pos = pos+eY*(mySize_y-winfs_y)/2; + vector win_sz; + win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos, + sprintf("%s^7 won the game!",playername), + winfs, 0, DRAWFLAG_NORMAL, 0.5); + + drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE); + + minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos, + sprintf("%s^7 won the game!",playername), + winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5); + } +} + + +// Required function, draw the game status panel +void c4_hud_status(vector pos, vector mySize) +{ + HUD_Panel_DrawBg(1); + vector ts; + ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, + hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); + + pos_y += ts_y; + mySize_y -= ts_y; + + vector player_fontsize = hud_fontsize * 1.75; + ts_y = ( mySize_y - 2*player_fontsize_y ) / 2; + ts_x = mySize_x; + vector mypos; + vector tile_size = '48 48 0'; + + mypos = pos; + if ( (active_minigame.minigame_flags&C4_TURN_TEAM) == 2 ) + mypos_y += player_fontsize_y + ts_y; + drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE); + mypos_y += player_fontsize_y; + drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE); + + entity e; + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_player" ) + { + mypos = pos; + if ( e.team == 2 ) + mypos_y += player_fontsize_y + ts_y; + minigame_drawcolorcodedstring_trunc(mySize_x,mypos, + GetPlayerName(e.minigame_playerslot-1), + player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + + mypos_y += player_fontsize_y; + drawpic( mypos, + minigame_texture(strcat("c4/piece",ftos(e.team))), + tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL ); + + mypos_x += tile_size_x; + } + } +} + +// Turn a set of flags into a help message +string c4_turn_to_string(int turnflags) +{ + if ( turnflags & C4_TURN_DRAW ) + return _("Draw"); + + if ( turnflags & C4_TURN_WIN ) + { + if ( (turnflags&C4_TURN_TEAM) != minigame_self.team ) + return _("You lost the game!"); + return _("You win!"); + } + + if ( (turnflags & C4_TURN_TEAM) != minigame_self.team ) + return _("Wait for your opponent to make their move"); + + if ( turnflags & C4_TURN_PLACE ) + return _("Click on the game board to place your piece"); + + return ""; +} + +// Make the correct move +void c4_make_move(entity minigame) +{ + if ( minigame.minigame_flags == (C4_TURN_PLACE|minigame_self.team) ) + { + minigame_cmd("move ",c4_curr_pos); + } +} + +void c4_set_curr_pos(string s) +{ + if ( c4_curr_pos ) + strunzone(c4_curr_pos); + if ( s ) + s = strzone(s); + c4_curr_pos = s; +} + +// Required function, handle client events +int c4_client_event(entity minigame, string event, ...) +{ + switch(event) + { + case "activate": + { + c4_set_curr_pos(""); + minigame.message = c4_turn_to_string(minigame.minigame_flags); + return false; + } + case "key_pressed": + { + if((minigame.minigame_flags & C4_TURN_TEAM) == minigame_self.team) + { + switch ( ...(0,int) ) + { + case K_RIGHTARROW: + case K_KP_RIGHTARROW: + if ( ! c4_curr_pos ) + c4_set_curr_pos(c4_get_lowest_tile(minigame, "a3")); + else + c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,1,0,C4_NUM_CNT,C4_LET_CNT))); + return true; + case K_LEFTARROW: + case K_KP_LEFTARROW: + if ( ! c4_curr_pos ) + c4_set_curr_pos(c4_get_lowest_tile(minigame, "c3")); + else + c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,-1,0,C4_NUM_CNT,C4_LET_CNT))); + return true; + /*case K_UPARROW: + case K_KP_UPARROW: + if ( ! c4_curr_pos ) + c4_set_curr_pos("a1"); + else + c4_set_curr_pos(minigame_relative_tile(c4_curr_pos,0,1,6,7)); + return true; + case K_DOWNARROW: + case K_KP_DOWNARROW: + if ( ! c4_curr_pos ) + c4_set_curr_pos("a3"); + else + c4_set_curr_pos(minigame_relative_tile(c4_curr_pos,0,-1,6,7)); + return true;*/ + case K_ENTER: + case K_KP_ENTER: + case K_SPACE: + c4_make_move(minigame); + return true; + } + } + + return false; + } + case "mouse_pressed": + { + if(...(0,int) == K_MOUSE1) + { + c4_make_move(minigame); + return true; + } + + return false; + } + case "mouse_moved": + { + vector mouse_pos = minigame_hud_normalize(mousepos,c4_boardpos,c4_boardsize); + if ( minigame.minigame_flags == (C4_TURN_PLACE|minigame_self.team) ) + { + c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_tile_name(mouse_pos,C4_NUM_CNT,C4_LET_CNT))); + } + if ( ! c4_valid_tile(c4_curr_pos) ) + c4_set_curr_pos(""); + + return true; + } + case "network_receive": + { + entity sent = ...(0,entity); + int sf = ...(1,int); + if ( sent.classname == "minigame" ) + { + if ( sf & MINIG_SF_UPDATE ) + { + sent.message = c4_turn_to_string(sent.minigame_flags); + if ( sent.minigame_flags & minigame_self.team ) + minigame_prompt(); + } + } + + return false; + } + } + + return false; +} + +#endif \ No newline at end of file diff --git a/qcsrc/common/minigames/minigame/pp.qc b/qcsrc/common/minigames/minigame/pp.qc new file mode 100644 index 0000000000..b81ce7a9cc --- /dev/null +++ b/qcsrc/common/minigames/minigame/pp.qc @@ -0,0 +1,587 @@ +const int PP_TURN_PLACE = 0x0100; // player has to place a piece on the board +const int PP_TURN_WIN = 0x0200; // player has won +const int PP_TURN_DRAW = 0x0400; // players have equal scores +const int PP_TURN_NEXT = 0x0800; // a player wants to start a new match +const int PP_TURN_TYPE = 0x0f00; // turn type mask + +const int PP_TURN_TEAM1 = 0x0001; +const int PP_TURN_TEAM2 = 0x0002; +const int PP_TURN_TEAM = 0x000f; // turn team mask + +const int PP_BLOCKED_TEAM = 5; // there won't ever be a 5th team, so we can abuse this + +.int pp_team1_score; +.int pp_team2_score; + +.int pp_nexteam; + +.entity pp_curr_piece; // identifier for the current target piece + +// find tic tac toe piece given its tile name +entity pp_find_piece(entity minig, string tile) +{ + entity e = world; + while ( ( e = findentity(e,owner,minig) ) ) + if ( e.classname == "minigame_board_piece" && e.netname == tile ) + return e; + return world; +} + +// check if the tile name is valid (3x3 grid) +bool pp_valid_tile(string tile) +{ + if ( !tile ) + return 0; + int number = minigame_tile_number(tile); + int letter = minigame_tile_letter(tile); + return 0 <= number && number < 7 && 0 <= letter && letter < 7; +} + +// Checks if the given piece completes a row +bool pp_winning_piece(entity piece) +{ + int number = minigame_tile_number(piece.netname); + int letter = minigame_tile_letter(piece.netname); + + // here goes + if(!pp_valid_tile(minigame_tile_buildname(letter-1,number)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter-1,number)).team == 5) + if(!pp_valid_tile(minigame_tile_buildname(letter+1,number)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter+1,number)).team == 5) + if(!pp_valid_tile(minigame_tile_buildname(letter,number-1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter,number-1)).team == 5) + if(!pp_valid_tile(minigame_tile_buildname(letter,number+1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter,number+1)).team == 5) + if(!pp_valid_tile(minigame_tile_buildname(letter+1,number+1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter+1,number+1)).team == 5) + if(!pp_valid_tile(minigame_tile_buildname(letter-1,number-1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter-1,number-1)).team == 5) + if(!pp_valid_tile(minigame_tile_buildname(letter+1,number-1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter+1,number-1)).team == 5) + if(!pp_valid_tile(minigame_tile_buildname(letter-1,number+1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter-1,number+1)).team == 5) + return true; + + return false; +} + +bool pp_valid_move(entity minigame, string pos) +{ + if(!pp_valid_tile(pos)) + return false; + if(pp_find_piece(minigame,pos).team == 5) + return false; + + entity current = minigame.pp_curr_piece; + if(!current) + return true; // no current piece? allow the move anywhere + + int number = minigame_tile_number(pos); + int letter = minigame_tile_letter(pos); + + if( (pp_find_piece(minigame,minigame_tile_buildname(letter-1,number)) == current) + || (pp_find_piece(minigame,minigame_tile_buildname(letter+1,number)) == current) + || (pp_find_piece(minigame,minigame_tile_buildname(letter,number-1)) == current) + || (pp_find_piece(minigame,minigame_tile_buildname(letter,number+1)) == current) + || (pp_find_piece(minigame,minigame_tile_buildname(letter+1,number+1)) == current) + || (pp_find_piece(minigame,minigame_tile_buildname(letter-1,number-1)) == current) + || (pp_find_piece(minigame,minigame_tile_buildname(letter+1,number-1)) == current) + || (pp_find_piece(minigame,minigame_tile_buildname(letter-1,number+1)) == current) + ) { return true; } + + return false; +} + +// make a move +void pp_move(entity minigame, entity player, string pos ) +{ + if ( minigame.minigame_flags & PP_TURN_PLACE ) + if ( pos && player.team == (minigame.minigame_flags & PP_TURN_TEAM) ) + { + if ( pp_valid_move(minigame,pos)) + { + entity existing = pp_find_piece(minigame,pos); + + if(existing && existing.team != 5) + { + if(existing.team == 1) + minigame.pp_team1_score++; + if(existing.team == 2) + minigame.pp_team2_score++; + } + + if(minigame.pp_curr_piece) + { + minigame.pp_curr_piece.cnt = 0; + minigame.pp_curr_piece.team = 5; + minigame_server_sendflags(minigame.pp_curr_piece,MINIG_SF_ALL); + } + + if(existing) + { + if(existing.netname) { strunzone(existing.netname); } + remove(existing); + } + + entity piece = msle_spawn(minigame,"minigame_board_piece"); + piece.cnt = 1; + piece.team = player.team; // temporary + piece.netname = strzone(pos); + minigame_server_sendflags(piece,MINIG_SF_ALL); + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + minigame.pp_nexteam = minigame_next_team(player.team,2); + minigame.pp_curr_piece = piece; + if ( pp_winning_piece(piece) ) + { + if(minigame.pp_team1_score == minigame.pp_team2_score) + minigame.minigame_flags = PP_TURN_DRAW; + else + minigame.minigame_flags = PP_TURN_WIN | ((minigame.pp_team1_score > minigame.pp_team2_score) ? 1 : 2); + } + else + minigame.minigame_flags = PP_TURN_PLACE | minigame.pp_nexteam; + } + } +} + +void pp_setup_pieces(entity minigame) +{ + int i, t; // letter, number + for(i = 0; i < 7; ++i) + for(t = 0; t < 7; ++t) + { + bool t2_true = ((i == 0 || i == 6) && t > 0 && t < 6); + bool t1_true = (i > 0 && i < 6 && (t == 0 || t == 6)); + + if(t1_true || t2_true) + { + entity piece = msle_spawn(minigame,"minigame_board_piece"); + piece.team = ((t1_true) ? 1 : 2); + piece.netname = strzone(minigame_tile_buildname(i,t)); + minigame_server_sendflags(piece,MINIG_SF_ALL); + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + } + } + + minigame.pp_curr_piece = world; +} + +// request a new match +void pp_next_match(entity minigame, entity player) +{ +#ifdef SVQC + // on multiplayer matches, wait for both players to agree + if ( minigame.minigame_flags & (PP_TURN_WIN|PP_TURN_DRAW) ) + { + minigame.minigame_flags = PP_TURN_NEXT | player.team; + minigame.SendFlags |= MINIG_SF_UPDATE; + } + else if ( (minigame.minigame_flags & PP_TURN_NEXT) && + !( minigame.minigame_flags & player.team ) ) +#endif + { + minigame.minigame_flags = PP_TURN_PLACE | minigame.pp_nexteam; + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + entity e = world; + while ( ( e = findentity(e,owner,minigame) ) ) + if ( e.classname == "minigame_board_piece" ) + remove(e); + minigame.pp_team1_score = 0; + minigame.pp_team2_score = 0; + + pp_setup_pieces(minigame); + } +} + +#ifdef SVQC + + +// required function, handle server side events +int pp_server_event(entity minigame, string event, ...) +{ + switch(event) + { + case "start": + { + minigame.minigame_flags = (PP_TURN_PLACE | PP_TURN_TEAM1); + pp_setup_pieces(minigame); + return true; + } + case "end": + { + entity e = world; + while( (e = findentity(e, owner, minigame)) ) + if(e.classname == "minigame_board_piece") + { + if(e.netname) { strunzone(e.netname); } + remove(e); + } + return false; + } + case "join": + { + int pl_num = minigame_count_players(minigame); + + // Don't allow more than 2 players + if(pl_num >= 2) { return false; } + + // Get the right team + if(minigame.minigame_players) + return minigame_next_team(minigame.minigame_players.team, 2); + + // Team 1 by default + return 1; + } + case "cmd": + { + switch(argv(0)) + { + case "move": + pp_move(minigame, ...(0,entity), ...(1,int) == 2 ? argv(1) : string_null ); + return true; + case "next": + pp_next_match(minigame,...(0,entity)); + return true; + } + + return false; + } + case "network_send": + { + entity sent = ...(0,entity); + int sf = ...(1,int); + if ( sent.classname == "minigame" && (sf & MINIG_SF_UPDATE ) ) + { + WriteByte(MSG_ENTITY,sent.pp_team1_score); + WriteByte(MSG_ENTITY,sent.pp_team2_score); + } + else if(sent.classname == "minigame_board_piece") + WriteByte(MSG_ENTITY,sent.cnt); + return false; + } + } + + return false; +} + + +#elif defined(CSQC) + +string pp_curr_pos; // identifier of the tile under the mouse +vector pp_boardpos; // HUD board position +vector pp_boardsize;// HUD board size +.int pp_checkwin; // Used to optimize checks to display a win + +// Required function, draw the game board +void pp_hud_board(vector pos, vector mySize) +{ + minigame_hud_fitsqare(pos, mySize); + pp_boardpos = pos; + pp_boardsize = mySize; + + minigame_hud_simpleboard(pos,mySize,minigame_texture("pp/board")); + + vector tile_size = minigame_hud_denormalize_size('1 1 0'/7,pos,mySize); + vector tile_pos; + + active_minigame.pp_curr_piece = world; + entity e; + FOREACH_MINIGAME_ENTITY(e) + if(e.classname == "minigame_board_piece") + if(e.cnt) + { + active_minigame.pp_curr_piece = e; + break; + } + + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_board_piece" ) + { + tile_pos = minigame_tile_pos(e.netname,7,7); + tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); + + vector tile_color = '1 1 1'; + switch(e.team) + { + case 1: tile_color = '1 0.3 0.3'; break; + case 2: tile_color = '0.3 0.3 1'; break; + // 3, 4 coming later? + } + + string tile_name = strcat("pp/piece",ftos(e.team)); + if(e.team == 5) { tile_name = "pp/piece_taken"; } + + if(e == active_minigame.pp_curr_piece) + { + tile_name = "pp/piece_current"; + + // draw the splat too + minigame_drawpic_centered( tile_pos, + minigame_texture("pp/piece_taken"), + tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL ); + } + + minigame_drawpic_centered( tile_pos, + minigame_texture(tile_name), + tile_size, tile_color, panel_fg_alpha, DRAWFLAG_NORMAL ); + } + } + + if ( (active_minigame.minigame_flags & PP_TURN_TEAM) == minigame_self.team ) + if ( pp_valid_move(active_minigame, pp_curr_pos) ) + { + tile_pos = minigame_tile_pos(pp_curr_pos,7,7); + tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); + minigame_drawpic_centered( tile_pos, + minigame_texture("pp/piece_current"), + tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL ); + } + else if(pp_valid_tile(pp_curr_pos)) + { + tile_pos = minigame_tile_pos(pp_curr_pos,7,7); + tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); + minigame_drawpic_centered( tile_pos, + minigame_texture("pp/piece_selected"), + tile_size, '1 1 1', panel_fg_alpha / 2, DRAWFLAG_NORMAL ); + } + + if ( active_minigame.minigame_flags & PP_TURN_WIN ) + { + vector winfs = hud_fontsize*2; + string playername = ""; + FOREACH_MINIGAME_ENTITY(e) + if ( e.classname == "minigame_player" && + e.team == (active_minigame.minigame_flags & PP_TURN_TEAM) ) + playername = GetPlayerName(e.minigame_playerslot-1); + + vector win_pos = pos+eY*(mySize_y-winfs_y)/2; + vector win_sz; + win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos, + sprintf("%s^7 won the game!",playername), + winfs, 0, DRAWFLAG_NORMAL, 0.5); + + drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE); + + minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos, + sprintf("%s^7 won the game!",playername), + winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5); + } +} + + +// Required function, draw the game status panel +void pp_hud_status(vector pos, vector mySize) +{ + HUD_Panel_DrawBg(1); + vector ts; + ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, + hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); + + pos_y += ts_y; + mySize_y -= ts_y; + + vector player_fontsize = hud_fontsize * 1.75; + ts_y = ( mySize_y - 2*player_fontsize_y ) / 2; + ts_x = mySize_x; + vector mypos; + vector tile_size = '48 48 0'; + + mypos = pos; + if ( (active_minigame.minigame_flags&PP_TURN_TEAM) == 2 ) + mypos_y += player_fontsize_y + ts_y; + drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE); + mypos_y += player_fontsize_y; + drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE); + + entity e; + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_player" ) + { + vector tile_color = '1 1 1'; + switch(e.team) + { + case 1: tile_color = '1 0.3 0.3'; break; + case 2: tile_color = '0.3 0.3 1'; break; + // 3, 4 coming later? + } + + mypos = pos; + if ( e.team == 2 ) + mypos_y += player_fontsize_y + ts_y; + minigame_drawcolorcodedstring_trunc(mySize_x,mypos, + GetPlayerName(e.minigame_playerslot-1), + player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + + mypos_y += player_fontsize_y; + drawpic( mypos, + minigame_texture(strcat("pp/piece",ftos(e.team))), + tile_size, tile_color, panel_fg_alpha, DRAWFLAG_NORMAL ); + + mypos_x += tile_size_x; + int myscore = 0; + if(e.team == 1) { myscore = active_minigame.pp_team1_score; } + if(e.team == 2) { myscore = active_minigame.pp_team2_score; } + + drawstring(mypos,ftos(myscore),tile_size, + '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } + } +} + +// Turn a set of flags into a help message +string pp_turn_to_string(int turnflags) +{ + if ( turnflags & PP_TURN_DRAW ) + return _("Draw"); + + if ( turnflags & PP_TURN_WIN ) + { + if ( (turnflags&PP_TURN_TEAM) != minigame_self.team ) + return _("You lost the game!\nSelect \"^1Next Match^7\" on the menu for a rematch!"); + return _("You win!\nSelect \"^1Next Match^7\" on the menu to start a new match!"); + } + + if ( turnflags & PP_TURN_NEXT ) + { + if ( (turnflags&PP_TURN_TEAM) != minigame_self.team ) + return _("Select \"^1Next Match^7\" on the menu to start a new match!"); + return _("Wait for your opponent to confirm the rematch"); + } + + if ( (turnflags & PP_TURN_TEAM) != minigame_self.team ) + return _("Wait for your opponent to make their move"); + + if ( turnflags & PP_TURN_PLACE ) + return _("Click on the game board to place your piece"); + + return ""; +} + +// Make the correct move +void pp_make_move(entity minigame) +{ + if ( minigame.minigame_flags == (PP_TURN_PLACE|minigame_self.team) ) + { + minigame_cmd("move ",pp_curr_pos); + } +} + +void pp_set_curr_pos(string s) +{ + if ( pp_curr_pos ) + strunzone(pp_curr_pos); + if ( s ) + s = strzone(s); + pp_curr_pos = s; +} + +// Required function, handle client events +int pp_client_event(entity minigame, string event, ...) +{ + switch(event) + { + case "activate": + { + pp_set_curr_pos(""); + minigame.message = pp_turn_to_string(minigame.minigame_flags); + return false; + } + case "key_pressed": + { + if((minigame.minigame_flags & PP_TURN_TEAM) == minigame_self.team) + { + switch ( ...(0,int) ) + { + case K_RIGHTARROW: + case K_KP_RIGHTARROW: + if ( ! pp_curr_pos ) + pp_set_curr_pos("a3"); + else + pp_set_curr_pos(minigame_relative_tile(pp_curr_pos,1,0,7,7)); + return true; + case K_LEFTARROW: + case K_KP_LEFTARROW: + if ( ! pp_curr_pos ) + pp_set_curr_pos("c3"); + else + pp_set_curr_pos(minigame_relative_tile(pp_curr_pos,-1,0,7,7)); + return true; + case K_UPARROW: + case K_KP_UPARROW: + if ( ! pp_curr_pos ) + pp_set_curr_pos("a1"); + else + pp_set_curr_pos(minigame_relative_tile(pp_curr_pos,0,1,7,7)); + return true; + case K_DOWNARROW: + case K_KP_DOWNARROW: + if ( ! pp_curr_pos ) + pp_set_curr_pos("a3"); + else + pp_set_curr_pos(minigame_relative_tile(pp_curr_pos,0,-1,7,7)); + return true; + case K_ENTER: + case K_KP_ENTER: + case K_SPACE: + pp_make_move(minigame); + return true; + } + } + + return false; + } + case "mouse_pressed": + { + if(...(0,int) == K_MOUSE1) + { + pp_make_move(minigame); + return true; + } + + return false; + } + case "mouse_moved": + { + vector mouse_pos = minigame_hud_normalize(mousepos,pp_boardpos,pp_boardsize); + if ( minigame.minigame_flags == (PP_TURN_PLACE|minigame_self.team) ) + pp_set_curr_pos(minigame_tile_name(mouse_pos,7,7)); + if ( ! pp_valid_tile(pp_curr_pos) ) + pp_set_curr_pos(""); + + return true; + } + case "network_receive": + { + entity sent = ...(0,entity); + int sf = ...(1,int); + if ( sent.classname == "minigame" ) + { + if ( sf & MINIG_SF_UPDATE ) + { + sent.message = pp_turn_to_string(sent.minigame_flags); + if ( sent.minigame_flags & minigame_self.team ) + minigame_prompt(); + sent.pp_team1_score = ReadByte(); + sent.pp_team2_score = ReadByte(); + } + } + else if(sent.classname == "minigame_board_piece") + { + sent.cnt = ReadByte(); + if(sent.cnt) + minigame.pp_curr_piece = sent; + } + + return false; + } + case "menu_show": + { + HUD_MinigameMenu_CustomEntry(...(0,entity),_("Next Match"),"next"); + return false; + } + case "menu_click": + { + if(...(0,string) == "next") + minigame_cmd("next"); + return false; + } + } + + return false; +} + +#endif \ No newline at end of file diff --git a/qcsrc/common/minigames/minigame/ps.qc b/qcsrc/common/minigames/minigame/ps.qc new file mode 100644 index 0000000000..3acef2fd5f --- /dev/null +++ b/qcsrc/common/minigames/minigame/ps.qc @@ -0,0 +1,620 @@ +const float PS_TURN_MOVE = 0x0100; // player has to click on a piece on the board +const float PS_TURN_WIN = 0x0200; // player has won +const float PS_TURN_DRAW = 0x0400; // player can make no more moves +const float PS_TURN_TYPE = 0x0f00; // turn type mask + +const int PS_LET_CNT = 7; +const int PS_NUM_CNT = 7; + +const int PS_TILE_SIZE = 8; + +// find same game piece given its tile name +entity ps_find_piece(entity minig, string tile) +{ + entity e = world; + while ( ( e = findentity(e,owner,minig) ) ) + if ( e.classname == "minigame_board_piece" && e.netname == tile ) + return e; + return world; +} + +bool ps_draw(entity minigame) +{ + int valid = 0; + entity e = world; + while( ( e = findentity(e,owner,minigame) ) ) + if( e.classname == "minigame_board_piece" ) + { + ++valid; + } + + return ((valid > 0) ? true : false); +} + +bool ps_tile_blacklisted(string tile) +{ + int number = minigame_tile_number(tile); + int letter = minigame_tile_letter(tile); + if(letter < 2) + if(number < 2) + return true; + else if(number > PS_NUM_CNT - 3) + return true; + if(letter > PS_LET_CNT - 3) + if(number < 2) + return true; + else if(number > PS_NUM_CNT - 3) + return true; + + return false; +} + +// check if the tile name is valid (5x5 grid) +bool ps_valid_tile(string tile) +{ + if ( !tile ) + return false; + if(ps_tile_blacklisted(tile)) + return false; + float number = minigame_tile_number(tile); + float letter = minigame_tile_letter(tile); + return 0 <= number && number < PS_NUM_CNT && 0 <= letter && letter < PS_LET_CNT; +} + +// Checks if the given piece completes a row +bool ps_winning_piece(entity minigame) +{ + //int number = minigame_tile_number(piece.netname); + //int letter = minigame_tile_letter(piece.netname); + + entity e = world; + while ( ( e = findentity(e,owner,minigame) ) ) + if ( e.classname == "minigame_board_piece" ) + { + int number = minigame_tile_number(e.netname); + int letter = minigame_tile_letter(e.netname); + string try = minigame_tile_buildname(letter - 1, number); + if(ps_find_piece(minigame,try)) + { + try = minigame_tile_buildname(letter - 2, number); + if(ps_valid_tile(try) && !ps_find_piece(minigame,try)) + return false; // a move is valid, abort! + } + try = minigame_tile_buildname(letter + 1, number); + if(ps_find_piece(minigame,try)) + { + try = minigame_tile_buildname(letter + 2, number); + if(ps_valid_tile(try) && !ps_find_piece(minigame,try)) + return false; // a move is valid, abort! + } + try = minigame_tile_buildname(letter, number - 1); + if(ps_find_piece(minigame,try)) + { + try = minigame_tile_buildname(letter, number - 2); + if(ps_valid_tile(try) && !ps_find_piece(minigame,try)) + return false; // a move is valid, abort! + } + try = minigame_tile_buildname(letter, number + 1); + if(ps_find_piece(minigame,try)) + { + try = minigame_tile_buildname(letter, number + 2); + if(ps_valid_tile(try) && !ps_find_piece(minigame,try)) + return false; // a move is valid, abort! + } + } + + return true; +} + +void ps_setup_pieces(entity minigame) +{ + int i, t; + for(i = 0; i < PS_NUM_CNT; ++i) + for(t = 0; t < PS_LET_CNT; ++t) + { + string try = minigame_tile_buildname(i,t); + if(!ps_valid_tile(try)) + continue; + if(i == floor(PS_NUM_CNT * 0.5) && t == floor(PS_LET_CNT * 0.5)) + continue; // middle piece is empty + entity piece = msle_spawn(minigame,"minigame_board_piece"); + piece.team = 1; // init default team? + piece.netname = strzone(minigame_tile_buildname(t,i)); + minigame_server_sendflags(piece,MINIG_SF_ALL); + } + + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); +} + +bool ps_move_piece(entity minigame, entity piece, string pos, int leti, int numb) +{ + if(!piece) + return false; + if(ps_find_piece(minigame, pos)) + return false; + entity middle = ps_find_piece(minigame, minigame_tile_buildname(leti,numb)); + if(!middle) + return false; + + if(middle.netname) { strunzone(middle.netname); } + remove(middle); + + if(piece.netname) { strunzone(piece.netname); } + piece.netname = strzone(pos); + + minigame_server_sendflags(piece,MINIG_SF_ALL); + + return true; +} + +// make a move +void ps_move(entity minigame, entity player, string thepiece, string pos ) +{ + if ( minigame.minigame_flags & PS_TURN_MOVE ) + if ( pos ) + { + if ( ps_valid_tile(pos) ) + if ( !ps_find_piece(minigame, pos) && ps_find_piece(minigame, thepiece) ) + { + entity piece = ps_find_piece(minigame, thepiece); + int number = minigame_tile_number(thepiece); + int letter = minigame_tile_letter(thepiece); + bool done = false; + string try; + + try = minigame_tile_buildname(letter-1,number); + if(ps_find_piece(minigame,try)) + { + try = minigame_tile_buildname(letter-2,number); + if(ps_valid_tile(try) && try == pos) + done = ps_move_piece(minigame, piece, pos, letter - 1, number); + } + try = minigame_tile_buildname(letter+1,number); + if(!done && ps_find_piece(minigame,try)) + { + try = minigame_tile_buildname(letter+2,number); + if(ps_valid_tile(try) && try == pos) + done = ps_move_piece(minigame, piece, pos, letter + 1, number); + } + try = minigame_tile_buildname(letter,number-1); + if(!done && ps_find_piece(minigame,try)) + { + try = minigame_tile_buildname(letter,number-2); + if(ps_valid_tile(try) && try == pos) + done = ps_move_piece(minigame, piece, pos, letter, number - 1); + } + try = minigame_tile_buildname(letter,number+1); + if(!done && ps_find_piece(minigame,try)) + { + try = minigame_tile_buildname(letter,number+2); + if(ps_valid_tile(try) && try == pos) + done = ps_move_piece(minigame, piece, pos, letter, number + 1); + } + + if(!done) + return; // didn't make a move + + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + + if ( ps_winning_piece(minigame) ) + { + if(ps_draw(minigame)) + minigame.minigame_flags = PS_TURN_DRAW; + else + minigame.minigame_flags = PS_TURN_WIN; + } + else + minigame.minigame_flags = PS_TURN_MOVE; + } + } +} + +#ifdef SVQC + + +// required function, handle server side events +int ps_server_event(entity minigame, string event, ...) +{ + switch(event) + { + case "start": + { + ps_setup_pieces(minigame); + minigame.minigame_flags = PS_TURN_MOVE; + return true; + } + case "end": + { + entity e = world; + while( (e = findentity(e, owner, minigame)) ) + if(e.classname == "minigame_board_piece") + { + if(e.netname) { strunzone(e.netname); } + remove(e); + } + return false; + } + case "join": + { + int pl_num = minigame_count_players(minigame); + + // Don't allow more than 1 player + if(pl_num >= 1) { return false; } + + // Team 1 by default + return 1; + } + case "cmd": + { + switch(argv(0)) + { + case "move": + + ps_move(minigame, ...(0,entity), (...(1,int) == 3 ? argv(1) : string_null), (...(1,int) == 3 ? argv(2) : string_null)); + return true; + } + + return false; + } + } + + return false; +} + + +#elif defined(CSQC) + +entity ps_curr_piece; // identifier for the currently selected piece +string ps_curr_pos; // identifier of the tile under the mouse +vector ps_boardpos; // HUD board position +vector ps_boardsize;// HUD board size + +// Required function, draw the game board +void ps_hud_board(vector pos, vector mySize) +{ + minigame_hud_fitsqare(pos, mySize); + ps_boardpos = pos; + ps_boardsize = mySize; + + minigame_hud_simpleboard(pos,mySize,minigame_texture("ps/board")); + + vector tile_size = minigame_hud_denormalize_size('1 1 0' / PS_TILE_SIZE,pos,mySize); + vector tile_pos; + + bool valid = ps_valid_tile(ps_curr_pos); + bool highlight = false; + if(valid) + { + string try; + int number = minigame_tile_number(ps_curr_pos); + int letter = minigame_tile_letter(ps_curr_pos); + try = minigame_tile_buildname(letter-1,number); + if(ps_find_piece(active_minigame,try)) + { + try = minigame_tile_buildname(letter-2,number); + if(ps_valid_tile(try) && !ps_find_piece(active_minigame,try)) + highlight = true; + } + try = minigame_tile_buildname(letter+1,number); + if(ps_find_piece(active_minigame,try)) + { + try = minigame_tile_buildname(letter+2,number); + if(ps_valid_tile(try) && !ps_find_piece(active_minigame,try)) + highlight = true; + } + try = minigame_tile_buildname(letter,number-1); + if(ps_find_piece(active_minigame,try)) + { + try = minigame_tile_buildname(letter,number-2); + if(ps_valid_tile(try) && !ps_find_piece(active_minigame,try)) + highlight = true; + } + try = minigame_tile_buildname(letter,number+1); + if(ps_find_piece(active_minigame,try)) + { + try = minigame_tile_buildname(letter,number+2); + if(ps_valid_tile(try) && !ps_find_piece(active_minigame,try)) + highlight = true; + } + } + bool draw_pos = false; + if(ps_curr_piece && valid && !ps_find_piece(active_minigame, ps_curr_pos)) + { + string try; // sigh + int numb = minigame_tile_number(ps_curr_piece.netname); + int leti = minigame_tile_letter(ps_curr_piece.netname); + + try = minigame_tile_buildname(leti-1,numb); + if(ps_find_piece(active_minigame,try)) + { + try = minigame_tile_buildname(leti-2,numb); + if(try == ps_curr_pos) + draw_pos = true; + } + try = minigame_tile_buildname(leti+1,numb); + if(ps_find_piece(active_minigame,try)) + { + try = minigame_tile_buildname(leti+2,numb); + if(try == ps_curr_pos) + draw_pos = true; + } + try = minigame_tile_buildname(leti,numb-1); + if(ps_find_piece(active_minigame,try)) + { + try = minigame_tile_buildname(leti,numb-2); + if(try == ps_curr_pos) + draw_pos = true; + } + try = minigame_tile_buildname(leti,numb+1); + if(ps_find_piece(active_minigame,try)) + { + try = minigame_tile_buildname(leti,numb+2); + if(try == ps_curr_pos) + draw_pos = true; + } + } + + entity e; + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_board_piece" ) + { + tile_pos = minigame_tile_pos(e.netname,PS_NUM_CNT,PS_LET_CNT); + tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); + + vector tile_color = '1 1 1'; + + if(highlight) + if(e.netname == ps_curr_pos) + if(ps_curr_piece.netname != ps_curr_pos) + { + minigame_drawpic_centered( tile_pos, + minigame_texture("ps/tile_available"), + tile_size, tile_color, panel_fg_alpha, DRAWFLAG_NORMAL ); + } + if(e == ps_curr_piece) + { + minigame_drawpic_centered( tile_pos, + minigame_texture("ps/tile_selected"), + tile_size, tile_color, panel_fg_alpha, DRAWFLAG_ADDITIVE ); + } + + minigame_drawpic_centered( tile_pos, + minigame_texture("ps/piece"), + tile_size * 0.8, tile_color, panel_fg_alpha, DRAWFLAG_NORMAL ); + } + } + + if(draw_pos) + { + tile_pos = minigame_tile_pos(ps_curr_pos,PS_NUM_CNT,PS_LET_CNT); + tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); + + minigame_drawpic_centered(tile_pos, + minigame_texture("ps/piece"), + tile_size * 0.8, '0.5 0.5 0.5', panel_fg_alpha, DRAWFLAG_NORMAL); + } + + if ( ( active_minigame.minigame_flags & PS_TURN_WIN ) || ( active_minigame.minigame_flags & PS_TURN_DRAW ) ) + { + int remaining = 0; + FOREACH_MINIGAME_ENTITY(e) + if(e.classname == "minigame_board_piece") + ++remaining; + + vector winfs = hud_fontsize*2; + string remaining_text; + if(active_minigame.minigame_flags & PS_TURN_WIN) + remaining_text = "All pieces cleared!"; + else + remaining_text = strcat("Remaining pieces: ", ftos(remaining)); + + vector win_pos = pos+eY*(mySize_y-winfs_y)/2; + vector win_sz; + win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos, + sprintf("Game over! %s", remaining_text), + winfs, 0, DRAWFLAG_NORMAL, 0.5); + + drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE); + + minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos, + sprintf("Game over! %s", remaining_text), + winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5); + } +} + + +// Required function, draw the game status panel +void ps_hud_status(vector pos, vector mySize) +{ + HUD_Panel_DrawBg(1); + vector ts; + ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, + hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); + + pos_y += ts_y; + mySize_y -= ts_y; + + vector player_fontsize = hud_fontsize * 1.75; + ts_y = ( mySize_y - 2*player_fontsize_y ) / 2; + ts_x = mySize_x; + vector mypos; + vector tile_size = '48 48 0'; + + mypos = pos; + drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE); + mypos_y += player_fontsize_y; + drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE); + + int remaining = 0; + entity e; + FOREACH_MINIGAME_ENTITY(e) + { + if(e.classname == "minigame_board_piece") + { + ++remaining; + } + } + + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_player" ) + { + mypos = pos; + minigame_drawcolorcodedstring_trunc(mySize_x,mypos, + GetPlayerName(e.minigame_playerslot-1), + player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + + mypos_y += player_fontsize_y; + //drawpic( mypos, + // minigame_texture("ps/piece"), + // tile_size, '1 0 0', panel_fg_alpha, DRAWFLAG_NORMAL ); + + //mypos_x += tile_size_x; + + drawstring(mypos,sprintf(_("Pieces left: %s"), ftos(remaining)),'28 28 0', + '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } + } +} + +// Turn a set of flags into a help message +string ps_turn_to_string(int turnflags) +{ + if (turnflags & PS_TURN_DRAW ) + return _("No more valid moves"); + + if ( turnflags & PS_TURN_WIN ) + return _("Well done, you win!"); + + if ( turnflags & PS_TURN_MOVE ) + return _("Jump a piece over another to capture it"); + + return ""; +} + +// Make the correct move +void ps_make_move(entity minigame) +{ + if ( minigame.minigame_flags == PS_TURN_MOVE ) + { + entity piece = ps_find_piece(minigame,ps_curr_pos); + if(!ps_curr_piece || piece) + ps_curr_piece = ps_find_piece(minigame,ps_curr_pos); + else + { + minigame_cmd("move ", ps_curr_piece.netname, " ", ps_curr_pos); + ps_curr_piece = world; + } + } +} + +void ps_set_curr_pos(string s) +{ + if ( ps_curr_pos ) + strunzone(ps_curr_pos); + if ( s ) + s = strzone(s); + ps_curr_pos = s; +} + +// Required function, handle client events +int ps_client_event(entity minigame, string event, ...) +{ + switch(event) + { + case "activate": + { + ps_set_curr_pos(""); + ps_curr_piece = world; + minigame.message = ps_turn_to_string(minigame.minigame_flags); + return false; + } + case "key_pressed": + { + //if((minigame.minigame_flags & PS_TURN_TEAM) == minigame_self.team) + { + switch ( ...(0,int) ) + { + case K_RIGHTARROW: + case K_KP_RIGHTARROW: + if ( ! ps_curr_pos ) + ps_set_curr_pos("a3"); + else + ps_set_curr_pos( minigame_relative_tile(ps_curr_pos,1,0,PS_NUM_CNT,PS_LET_CNT)); + return true; + case K_LEFTARROW: + case K_KP_LEFTARROW: + if ( ! ps_curr_pos ) + ps_set_curr_pos("c3"); + else + ps_set_curr_pos(minigame_relative_tile(ps_curr_pos,-1,0,PS_NUM_CNT,PS_LET_CNT)); + return true; + case K_UPARROW: + case K_KP_UPARROW: + if ( ! ps_curr_pos ) + ps_set_curr_pos("a1"); + else + ps_set_curr_pos(minigame_relative_tile(ps_curr_pos,0,1,PS_NUM_CNT,PS_LET_CNT)); + return true; + case K_DOWNARROW: + case K_KP_DOWNARROW: + if ( ! ps_curr_pos ) + ps_set_curr_pos("a3"); + else + ps_set_curr_pos(minigame_relative_tile(ps_curr_pos,0,-1,PS_NUM_CNT,PS_LET_CNT)); + return true; + case K_ENTER: + case K_KP_ENTER: + case K_SPACE: + ps_make_move(minigame); + return true; + } + } + + return false; + } + case "mouse_pressed": + { + if(...(0,int) == K_MOUSE1) + { + ps_make_move(minigame); + return true; + } + + return false; + } + case "mouse_moved": + { + vector mouse_pos = minigame_hud_normalize(mousepos,ps_boardpos,ps_boardsize); + if ( minigame.minigame_flags == PS_TURN_MOVE ) + { + ps_set_curr_pos(minigame_tile_name(mouse_pos,PS_NUM_CNT,PS_LET_CNT)); + } + if ( ! ps_valid_tile(ps_curr_pos) ) + ps_set_curr_pos(""); + + return true; + } + case "network_receive": + { + entity sent = ...(0,entity); + int sf = ...(1,int); + if ( sent.classname == "minigame" ) + { + if ( sf & MINIG_SF_UPDATE ) + { + sent.message = ps_turn_to_string(sent.minigame_flags); + if ( sent.minigame_flags & minigame_self.team ) + minigame_prompt(); + } + } + + return false; + } + } + + return false; +} + +#endif \ No newline at end of file diff --git a/qcsrc/common/minigames/minigame/qto.qc b/qcsrc/common/minigames/minigame/qto.qc new file mode 100644 index 0000000000..cef71dfcd3 --- /dev/null +++ b/qcsrc/common/minigames/minigame/qto.qc @@ -0,0 +1,461 @@ +const float QTO_TURN_MOVE = 0x0100; // player has to click on a piece on the board +const float QTO_TURN_WIN = 0x0200; // player has won +const float QTO_TURN_TYPE = 0x0f00; // turn type mask + +const int QTO_SF_PLAYERSCORE = MINIG_SF_CUSTOM; + +const int QTO_LET_CNT = 5; +const int QTO_NUM_CNT = 5; + +const int QTO_TILE_SIZE = 8; + +.int qto_moves; + +// find same game piece given its tile name +entity qto_find_piece(entity minig, string tile) +{ + entity e = world; + while ( ( e = findentity(e,owner,minig) ) ) + if ( e.classname == "minigame_board_piece" && e.netname == tile ) + return e; + return world; +} + +// Checks if the given piece completes a row +bool qto_winning_piece(entity minigame) +{ + //int number = minigame_tile_number(piece.netname); + //int letter = minigame_tile_letter(piece.netname); + + entity e = world; + while ( ( e = findentity(e,owner,minigame) ) ) + if ( e.classname == "minigame_board_piece" ) + { + if(!e.cnt) + return false; + } + + return true; +} + +// check if the tile name is valid (5x5 grid) +bool qto_valid_tile(string tile) +{ + if ( !tile ) + return false; + float number = minigame_tile_number(tile); + float letter = minigame_tile_letter(tile); + return 0 <= number && number < QTO_NUM_CNT && 0 <= letter && letter < QTO_LET_CNT; +} + +void qto_setup_pieces(entity minigame) +{ + int i, t; + for(i = 0; i < QTO_NUM_CNT; ++i) + for(t = 0; t < QTO_LET_CNT; ++t) + { + entity piece = msle_spawn(minigame,"minigame_board_piece"); + piece.team = 1; // init default team? + piece.cnt = 0; // initialize cnt + piece.netname = strzone(minigame_tile_buildname(t,i)); + minigame_server_sendflags(piece,MINIG_SF_ALL); + } + + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); +} + +void qto_add_score(entity minigame, int thescore) +{ +#ifdef SVQC + if(!minigame) + return; + if(minigame.minigame_players) + { + minigame.minigame_players.qto_moves += thescore; + minigame.minigame_players.SendFlags |= QTO_SF_PLAYERSCORE; + } +#endif +} + +// make a move +void qto_move(entity minigame, entity player, string pos ) +{ + if ( minigame.minigame_flags & QTO_TURN_MOVE ) + if ( pos ) + { + if ( qto_valid_tile(pos) ) + if ( qto_find_piece(minigame, pos) ) + { + entity piece; + #define DO_JUNK \ + if(piece) \ + { \ + piece.cnt = (piece.cnt) ? 0 : 1; \ + minigame_server_sendflags(piece,MINIG_SF_UPDATE); \ + } + + int number = minigame_tile_number(pos); + int letter = minigame_tile_letter(pos); + piece = qto_find_piece(minigame, pos); + DO_JUNK + piece = qto_find_piece(minigame, minigame_tile_buildname(letter-1,number)); + DO_JUNK + piece = qto_find_piece(minigame, minigame_tile_buildname(letter+1,number)); + DO_JUNK + piece = qto_find_piece(minigame, minigame_tile_buildname(letter,number-1)); + DO_JUNK + piece = qto_find_piece(minigame, minigame_tile_buildname(letter,number+1)); + DO_JUNK + + qto_add_score(minigame,1); // add 1 move score + + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + + if ( qto_winning_piece(minigame) ) + { + minigame.minigame_flags = QTO_TURN_WIN; + } + else + minigame.minigame_flags = QTO_TURN_MOVE; + } + } +} + +// restart match +void qto_restart_match(entity minigame, entity player) +{ + minigame.minigame_flags = QTO_TURN_MOVE; + minigame_server_sendflags(minigame,MINIG_SF_UPDATE); + entity e = world; + while ( ( e = findentity(e,owner,minigame) ) ) + if ( e.classname == "minigame_board_piece" ) + remove(e); + + qto_setup_pieces(minigame); +#ifdef SVQC + if(minigame.minigame_players) + { + minigame.minigame_players.qto_moves = 0; + minigame.minigame_players.SendFlags |= QTO_SF_PLAYERSCORE; + } +#endif +} + +#ifdef SVQC + + +// required function, handle server side events +int qto_server_event(entity minigame, string event, ...) +{ + switch(event) + { + case "start": + { + qto_setup_pieces(minigame); + minigame.minigame_flags = QTO_TURN_MOVE; + return true; + } + case "end": + { + entity e = world; + while( (e = findentity(e, owner, minigame)) ) + if(e.classname == "minigame_board_piece") + { + if(e.netname) { strunzone(e.netname); } + remove(e); + } + return false; + } + case "join": + { + int pl_num = minigame_count_players(minigame); + + // Don't allow more than 1 player + if(pl_num >= 1) { return false; } + + // Team 1 by default + return 1; + } + case "cmd": + { + switch(argv(0)) + { + case "move": + qto_move(minigame, ...(0,entity), ...(1,int) == 2 ? argv(1) : string_null ); + return true; + case "restart": + qto_restart_match(minigame,...(0,entity)); + return true; + } + + return false; + } + case "network_send": + { + entity sent = ...(0,entity); + int sf = ...(1,int); + if ( sent.classname == "minigame_board_piece" && (sf & MINIG_SF_UPDATE) ) + { + WriteByte(MSG_ENTITY,sent.cnt); + } + else if ( sent.classname == "minigame_player" && (sf & QTO_SF_PLAYERSCORE ) ) + { + WriteLong(MSG_ENTITY,sent.qto_moves); + } + return false; + } + } + + return false; +} + + +#elif defined(CSQC) + +string qto_curr_pos; // identifier of the tile under the mouse +vector qto_boardpos; // HUD board position +vector qto_boardsize;// HUD board size + +// Required function, draw the game board +void qto_hud_board(vector pos, vector mySize) +{ + minigame_hud_fitsqare(pos, mySize); + qto_boardpos = pos; + qto_boardsize = mySize; + + minigame_hud_simpleboard(pos,mySize,minigame_texture("qto/board")); + + vector tile_size = minigame_hud_denormalize_size('1 1 0' / QTO_TILE_SIZE,pos,mySize); + vector tile_pos; + + bool valid = qto_valid_tile(qto_curr_pos); + int number = minigame_tile_number(qto_curr_pos); + int letter = minigame_tile_letter(qto_curr_pos); + string pos1 = minigame_tile_buildname(letter-1,number); + string pos2 = minigame_tile_buildname(letter+1,number); + string pos3 = minigame_tile_buildname(letter,number-1); + string pos4 = minigame_tile_buildname(letter,number+1); + + entity e; + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_board_piece" ) + { + tile_pos = minigame_tile_pos(e.netname,QTO_NUM_CNT,QTO_LET_CNT); + tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); + + vector tile_color = '0.4 0.4 0.4'; + + if(valid) + switch(e.netname) + { + case qto_curr_pos: + case pos1: case pos2: case pos3: case pos4: + tile_color = '0.8 0.8 0.8'; + break; + } + + minigame_drawpic_centered( tile_pos, + minigame_texture(strcat("qto/piece", ftos(e.cnt))), + tile_size, tile_color, panel_fg_alpha, DRAWFLAG_NORMAL ); + } + } +} + + +// Required function, draw the game status panel +void qto_hud_status(vector pos, vector mySize) +{ + HUD_Panel_DrawBg(1); + vector ts; + ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, + hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); + + pos_y += ts_y; + mySize_y -= ts_y; + + vector player_fontsize = hud_fontsize * 1.75; + ts_y = ( mySize_y - 2*player_fontsize_y ) / 2; + ts_x = mySize_x; + vector mypos; + vector tile_size = '48 48 0'; + + mypos = pos; + drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE); + mypos_y += player_fontsize_y; + drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE); + + entity e; + FOREACH_MINIGAME_ENTITY(e) + { + if ( e.classname == "minigame_player" ) + { + mypos = pos; + minigame_drawcolorcodedstring_trunc(mySize_x,mypos, + GetPlayerName(e.minigame_playerslot-1), + player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + + mypos_y += player_fontsize_y; + //drawpic( mypos, + // minigame_texture("qto/piece"), + // tile_size, '1 0 0', panel_fg_alpha, DRAWFLAG_NORMAL ); + + //mypos_x += tile_size_x; + + drawstring(mypos,sprintf(_("Moves: %s"), ftos(e.qto_moves)),'32 32 0', + '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } + } +} + +// Turn a set of flags into a help message +string qto_turn_to_string(int turnflags) +{ + if ( turnflags & QTO_TURN_WIN ) + return _("Well done, you win!"); + + if ( turnflags & QTO_TURN_MOVE ) + return _("Turn all the angry faces into happy faces"); + + return ""; +} + +// Make the correct move +void qto_make_move(entity minigame) +{ + if ( minigame.minigame_flags == QTO_TURN_MOVE ) + { + minigame_cmd("move ",qto_curr_pos); + } +} + +void qto_set_curr_pos(string s) +{ + if ( qto_curr_pos ) + strunzone(qto_curr_pos); + if ( s ) + s = strzone(s); + qto_curr_pos = s; +} + +// Required function, handle client events +int qto_client_event(entity minigame, string event, ...) +{ + switch(event) + { + case "activate": + { + qto_set_curr_pos(""); + minigame.message = qto_turn_to_string(minigame.minigame_flags); + return false; + } + case "key_pressed": + { + //if((minigame.minigame_flags & QTO_TURN_TEAM) == minigame_self.team) + { + switch ( ...(0,int) ) + { + case K_RIGHTARROW: + case K_KP_RIGHTARROW: + if ( ! qto_curr_pos ) + qto_set_curr_pos("a3"); + else + qto_set_curr_pos( minigame_relative_tile(qto_curr_pos,1,0,QTO_NUM_CNT,QTO_LET_CNT)); + return true; + case K_LEFTARROW: + case K_KP_LEFTARROW: + if ( ! qto_curr_pos ) + qto_set_curr_pos("c3"); + else + qto_set_curr_pos(minigame_relative_tile(qto_curr_pos,-1,0,QTO_NUM_CNT,QTO_LET_CNT)); + return true; + case K_UPARROW: + case K_KP_UPARROW: + if ( ! qto_curr_pos ) + qto_set_curr_pos("a1"); + else + qto_set_curr_pos(minigame_relative_tile(qto_curr_pos,0,1,QTO_NUM_CNT,QTO_LET_CNT)); + return true; + case K_DOWNARROW: + case K_KP_DOWNARROW: + if ( ! qto_curr_pos ) + qto_set_curr_pos("a3"); + else + qto_set_curr_pos(minigame_relative_tile(qto_curr_pos,0,-1,QTO_NUM_CNT,QTO_LET_CNT)); + return true; + case K_ENTER: + case K_KP_ENTER: + case K_SPACE: + qto_make_move(minigame); + return true; + } + } + + return false; + } + case "mouse_pressed": + { + if(...(0,int) == K_MOUSE1) + { + qto_make_move(minigame); + return true; + } + + return false; + } + case "mouse_moved": + { + vector mouse_pos = minigame_hud_normalize(mousepos,qto_boardpos,qto_boardsize); + if ( minigame.minigame_flags == QTO_TURN_MOVE ) + { + qto_set_curr_pos(minigame_tile_name(mouse_pos,QTO_NUM_CNT,QTO_LET_CNT)); + } + if ( ! qto_valid_tile(qto_curr_pos) ) + qto_set_curr_pos(""); + + return true; + } + case "network_receive": + { + entity sent = ...(0,entity); + int sf = ...(1,int); + if ( sent.classname == "minigame" ) + { + if ( sf & MINIG_SF_UPDATE ) + { + sent.message = qto_turn_to_string(sent.minigame_flags); + if ( sent.minigame_flags & minigame_self.team ) + minigame_prompt(); + } + } + else if(sent.classname == "minigame_board_piece") + { + if(sf & MINIG_SF_UPDATE) + { + sent.cnt = ReadByte(); + } + } + else if ( sent.classname == "minigame_player" && (sf & QTO_SF_PLAYERSCORE ) ) + { + sent.qto_moves = ReadLong(); + } + + return false; + } + case "menu_show": + { + HUD_MinigameMenu_CustomEntry(...(0,entity),_("Restart"),"restart"); + return false; + } + case "menu_click": + { + if(...(0,string) == "restart") + minigame_cmd("restart"); + return false; + } + } + + return false; +} + +#endif \ No newline at end of file diff --git a/qcsrc/common/minigames/minigames.qc b/qcsrc/common/minigames/minigames.qc index a8daa44849..61ab9a6ba4 100644 --- a/qcsrc/common/minigames/minigames.qc +++ b/qcsrc/common/minigames/minigames.qc @@ -68,6 +68,12 @@ int minigame_next_team(int curr_team, int n_teams) return curr_team % n_teams + 1; } +// Get the previous team number +int minigame_prev_team(int curr_team, int n_teams) +{ + return curr_team % n_teams - 1; +} + // set send flags only when on server // (for example in game logic which can be used both in client and server void minigame_server_sendflags(entity ent, int mgflags) @@ -128,4 +134,4 @@ int minigame_count_players(entity minigame) #endif pl_num++; return pl_num; -} \ No newline at end of file +} diff --git a/qcsrc/common/minigames/minigames.qh b/qcsrc/common/minigames/minigames.qh index 6d3201c51c..86fb778dcb 100644 --- a/qcsrc/common/minigames/minigames.qh +++ b/qcsrc/common/minigames/minigames.qh @@ -34,6 +34,9 @@ string minigame_tile_name(vector pos, int rows, int columns); // Get the next team number (note: team numbers are between 1 and n_teams, inclusive) int minigame_next_team(int curr_team, int n_teams); +// Get the previous team number +int minigame_prev_team(int curr_team, int n_teams); + // set send flags only when on server // (for example in game logic which can be used both in client and server void minigame_server_sendflags(entity ent, int mgflags);