]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Add the new minigames
authorMario <zacjardine@y7mail.com>
Wed, 8 Jul 2015 15:02:49 +0000 (01:02 +1000)
committerMario <zacjardine@y7mail.com>
Wed, 8 Jul 2015 15:02:49 +0000 (01:02 +1000)
57 files changed:
gfx/hud/default/minigames/c4/board.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/board_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/board_over.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/board_over_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/board_under.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/board_under_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/icon.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/icon_notif.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/icon_notif_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/piece1.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/piece1_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/piece2.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/piece2_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/winglow.jpg [new file with mode: 0644]
gfx/hud/default/minigames/c4/winglow_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/board.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/board_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/icon.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/icon_notif.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/icon_notif_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/piece1.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/piece1_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/piece2.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/piece2_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/piece_current.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/piece_current_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/piece_selected.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/piece_selected_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/piece_taken.jpg [new file with mode: 0644]
gfx/hud/default/minigames/pp/piece_taken_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/ps/board.jpg [new file with mode: 0644]
gfx/hud/default/minigames/ps/board_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/ps/icon.jpg [new file with mode: 0644]
gfx/hud/default/minigames/ps/icon_notif.jpg [new file with mode: 0644]
gfx/hud/default/minigames/ps/icon_notif_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/ps/piece.jpg [new file with mode: 0644]
gfx/hud/default/minigames/ps/piece_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/ps/tile_available.jpg [new file with mode: 0644]
gfx/hud/default/minigames/ps/tile_available_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/ps/tile_selected.jpg [new file with mode: 0644]
gfx/hud/default/minigames/ps/tile_selected_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/qto/board.jpg [new file with mode: 0644]
gfx/hud/default/minigames/qto/board_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/qto/icon.jpg [new file with mode: 0644]
gfx/hud/default/minigames/qto/icon_notif.jpg [new file with mode: 0644]
gfx/hud/default/minigames/qto/icon_notif_alpha.jpg [new file with mode: 0644]
gfx/hud/default/minigames/qto/piece0.tga [new file with mode: 0644]
gfx/hud/default/minigames/qto/piece1.tga [new file with mode: 0644]
qcsrc/common/minigames/cl_minigames.qc
qcsrc/common/minigames/cl_minigames_hud.qc
qcsrc/common/minigames/minigame/all.qh
qcsrc/common/minigames/minigame/c4.qc [new file with mode: 0644]
qcsrc/common/minigames/minigame/pp.qc [new file with mode: 0644]
qcsrc/common/minigames/minigame/ps.qc [new file with mode: 0644]
qcsrc/common/minigames/minigame/qto.qc [new file with mode: 0644]
qcsrc/common/minigames/minigames.qc
qcsrc/common/minigames/minigames.qh

diff --git a/gfx/hud/default/minigames/c4/board.jpg b/gfx/hud/default/minigames/c4/board.jpg
new file mode 100644 (file)
index 0000000..8ab7a8d
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 (file)
index 0000000..416a858
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 (file)
index 0000000..9025039
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 (file)
index 0000000..4befedf
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 (file)
index 0000000..3f364b3
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 (file)
index 0000000..22c9bd1
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 (file)
index 0000000..85cf11f
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 (file)
index 0000000..f280450
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 (file)
index 0000000..040990f
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 (file)
index 0000000..00a869b
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 (file)
index 0000000..6b45fa1
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 (file)
index 0000000..e1f14a5
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 (file)
index 0000000..6b45fa1
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 (file)
index 0000000..bc21db0
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 (file)
index 0000000..02c5ff8
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 (file)
index 0000000..b435e3d
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 (file)
index 0000000..14938ed
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 (file)
index 0000000..7889f1b
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 (file)
index 0000000..b036d95
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 (file)
index 0000000..040990f
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 (file)
index 0000000..9bb789d
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 (file)
index 0000000..9bb789d
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 (file)
index 0000000..7701f42
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 (file)
index 0000000..7701f42
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 (file)
index 0000000..02afe48
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 (file)
index 0000000..02afe48
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 (file)
index 0000000..eaf9970
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 (file)
index 0000000..71d65fd
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 (file)
index 0000000..cafa6c3
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 (file)
index 0000000..cafa6c3
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 (file)
index 0000000..882e4d2
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 (file)
index 0000000..43f18b4
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 (file)
index 0000000..1390c89
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 (file)
index 0000000..b9024ae
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 (file)
index 0000000..040990f
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 (file)
index 0000000..210389d
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 (file)
index 0000000..6b45fa1
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 (file)
index 0000000..83551d7
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 (file)
index 0000000..e265d34
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 (file)
index 0000000..797aee9
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 (file)
index 0000000..e265d34
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 (file)
index 0000000..cf8fe0a
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 (file)
index 0000000..44a8708
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 (file)
index 0000000..a534f48
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 (file)
index 0000000..90e0e2e
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 (file)
index 0000000..040990f
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 (file)
index 0000000..fad7dbd
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 (file)
index 0000000..80a34c2
Binary files /dev/null and b/gfx/hud/default/minigames/qto/piece1.tga differ
index 0bac54c67863df0689c02310f485cdc51cfb4dbe..e8eee262459fbc9b46a26bea1ba50bf0f4890fce 100644 (file)
@@ -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
+}
index 4850224d95169ddc503a7141ef61c0593215906f..b7b7df128a501174a4faf43a220b3826d449e9b8 100644 (file)
@@ -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;
index a63d3335fe47ce91e90c6b156cb2c38f9b0125f7..d1899e7594328e0414b79ea37bdfbe1f093a410b 100644 (file)
@@ -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 (file)
index 0000000..a153b39
--- /dev/null
@@ -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 (file)
index 0000000..b81ce7a
--- /dev/null
@@ -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 (file)
index 0000000..3acef2f
--- /dev/null
@@ -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 (file)
index 0000000..cef71df
--- /dev/null
@@ -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
index a8daa44849b361676a6c36e1bd9bafcf16dc1bfe..61ab9a6ba40a361cf72c974c7ba043be8217714a 100644 (file)
@@ -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
+}
index 6d3201c51cb9942f0061f9b7687ea7f160f152c4..86fb778dcbc6f116da25b2610a66de9d82aa625d 100644 (file)
@@ -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);