Skip to content

Commit abcd46d

Browse files
committed
sprites and tiles
1 parent 56884c8 commit abcd46d

9 files changed

Lines changed: 287 additions & 0 deletions

File tree

examples/alien-flame.png

14 Bytes
Loading

examples/alien.png

12 Bytes
Loading

examples/enemy-sprites.png

1.99 KB
Loading

examples/missiles.png

732 Bytes
Loading

examples/tank.png

397 Bytes
Loading

examples/walls.png

1.57 KB
Loading

gfx/gfx_gamepad.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,17 @@ int gfx_gamepad_axis_scaled(int port, int axis_code)
9494

9595
#elif defined(GFX_VIDEO)
9696
#include "raylib.h"
97+
#include <stdio.h>
9798

9899
int gfx_gamepad_button_down(int port, int button_code)
99100
{
101+
static int probed = 0;
102+
if (!probed) {
103+
probed = 1;
104+
fprintf(stderr, "[JOY probe] avail(0)=%d avail(1)=%d name0=%s\n",
105+
IsGamepadAvailable(0), IsGamepadAvailable(1),
106+
IsGamepadAvailable(0) ? GetGamepadName(0) : "(none)");
107+
}
100108
if (port < 0 || port > 3) {
101109
return 0;
102110
}
4.44 KB
Binary file not shown.
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
<?php
2+
/**
3+
* Plugin Name: Pamela Offer Redirect
4+
* Description: Time-window redirects for offer pages, with admin editor
5+
* Version: 1.1.0
6+
* Author: Chris Garrett
7+
*/
8+
9+
10+
/* Add ?offer_debug=1 trace to verify the hook is firing on Kinsta. */
11+
12+
if (!defined('ABSPATH')) { exit; }
13+
14+
const PAMELA_OFFER_OPTION = 'pamela_offer_redirects';
15+
16+
/**
17+
* Default offers used the first time the plugin runs (before the admin saves anything).
18+
*/
19+
function pamela_offer_defaults() {
20+
return [
21+
[
22+
'slug' => 'reiki-professional-bundle',
23+
'start' => '2026-04-21 00:00:00',
24+
'end' => '2026-04-22 23:59:59',
25+
'redirect_to' => 'reiki-professional-bundle-final',
26+
],
27+
[
28+
'slug' => 'reiki-professional-bundle-final',
29+
'start' => '2026-04-23 00:00:00',
30+
'end' => '2026-04-24 23:59:59',
31+
'redirect_to' => 'reiki-professional-academy',
32+
],
33+
[
34+
'slug' => 'reiki-professional-bundle-reopen',
35+
'start' => '2026-04-26 00:00:00',
36+
'end' => '2026-04-27 23:59:59',
37+
'redirect_to' => 'reiki-professional-academy',
38+
],
39+
];
40+
}
41+
42+
function pamela_offer_get_offers() {
43+
$stored = get_option(PAMELA_OFFER_OPTION, null);
44+
if (!is_array($stored)) {
45+
return pamela_offer_defaults();
46+
}
47+
return $stored;
48+
}
49+
50+
/* ------------------------------------------------------------------ */
51+
/* Frontend: header markers + template_redirect logic */
52+
/* ------------------------------------------------------------------ */
53+
54+
add_action('send_headers', function () {
55+
if (!headers_sent()) {
56+
header('X-Pamela-Offer-Redirect: loaded');
57+
}
58+
});
59+
60+
add_action('template_redirect', function () {
61+
if (is_admin()) { return; }
62+
63+
$debug = isset($_GET['offer_debug']);
64+
$log = [];
65+
$emit = function ($msg) use (&$log) {
66+
error_log('[pamela-offer] ' . $msg);
67+
$log[] = $msg;
68+
};
69+
70+
$wp_timezone = wp_timezone();
71+
$now = new DateTime('now', $wp_timezone);
72+
73+
$request_path = trim(parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH), '/');
74+
75+
$offers = pamela_offer_get_offers();
76+
77+
$emit('template_redirect fired');
78+
$emit('Request path: ' . $request_path);
79+
$emit('Now: ' . $now->format('Y-m-d H:i:s T'));
80+
81+
if (!headers_sent()) {
82+
header('X-Pamela-Offer-Fired: yes');
83+
header('X-Pamela-Offer-Path: ' . $request_path);
84+
}
85+
86+
foreach ($offers as $offer) {
87+
if (empty($offer['slug'])) { continue; }
88+
89+
$matches_page = is_page($offer['slug']);
90+
$matches_path = ($request_path === trim($offer['slug'], '/'));
91+
92+
$emit(
93+
'Checking ' . $offer['slug'] .
94+
' | is_page=' . ($matches_page ? 'yes' : 'no') .
95+
' | path_match=' . ($matches_path ? 'yes' : 'no')
96+
);
97+
98+
if ($matches_page || $matches_path) {
99+
try {
100+
$start = new DateTime($offer['start'], $wp_timezone);
101+
$end = new DateTime($offer['end'], $wp_timezone);
102+
} catch (Exception $e) {
103+
$emit('Bad date on ' . $offer['slug'] . ': ' . $e->getMessage());
104+
continue;
105+
}
106+
107+
$emit('Start: ' . $start->format('Y-m-d H:i:s T'));
108+
$emit('End: ' . $end->format('Y-m-d H:i:s T'));
109+
110+
if ($now < $start || $now > $end) {
111+
$target = home_url('/' . trim($offer['redirect_to'], '/') . '/');
112+
$emit('Redirecting to: ' . $target);
113+
114+
if ($debug) {
115+
header('Content-Type: text/plain; charset=utf-8');
116+
echo "PAMELA OFFER REDIRECT — DEBUG MODE\n";
117+
echo "Would redirect to: {$target}\n\n";
118+
echo implode("\n", $log), "\n";
119+
exit;
120+
}
121+
122+
nocache_headers();
123+
wp_safe_redirect($target, 302);
124+
exit;
125+
}
126+
127+
$emit('Within active window, no redirect');
128+
}
129+
}
130+
131+
if ($debug) {
132+
header('Content-Type: text/plain; charset=utf-8');
133+
echo "PAMELA OFFER REDIRECT — DEBUG MODE\n";
134+
echo "No matching offer slug / no redirect triggered.\n\n";
135+
echo implode("\n", $log), "\n";
136+
exit;
137+
}
138+
}, 1);
139+
140+
/* ------------------------------------------------------------------ */
141+
/* Admin: Settings → Offer Redirects */
142+
/* ------------------------------------------------------------------ */
143+
144+
add_action('admin_menu', function () {
145+
add_options_page(
146+
'Offer Redirects',
147+
'Offer Redirects',
148+
'manage_options',
149+
'pamela-offer-redirect',
150+
'pamela_offer_render_admin_page'
151+
);
152+
});
153+
154+
add_action('admin_post_pamela_offer_save', 'pamela_offer_handle_save');
155+
156+
function pamela_offer_handle_save() {
157+
if (!current_user_can('manage_options')) {
158+
wp_die('Not allowed.');
159+
}
160+
check_admin_referer('pamela_offer_save');
161+
162+
$rows = $_POST['offers'] ?? [];
163+
$clean = [];
164+
165+
if (is_array($rows)) {
166+
foreach ($rows as $row) {
167+
$slug = sanitize_title(trim($row['slug'] ?? ''));
168+
$start = trim($row['start'] ?? '');
169+
$end = trim($row['end'] ?? '');
170+
$redirect_to = sanitize_title(trim($row['redirect_to'] ?? ''));
171+
172+
if ($slug === '' && $redirect_to === '' && $start === '' && $end === '') {
173+
continue; // blank row
174+
}
175+
if ($slug === '' || $redirect_to === '' || $start === '' || $end === '') {
176+
continue; // skip incomplete — could add notice, but simplest is to drop
177+
}
178+
179+
$clean[] = [
180+
'slug' => $slug,
181+
'start' => $start,
182+
'end' => $end,
183+
'redirect_to' => $redirect_to,
184+
];
185+
}
186+
}
187+
188+
update_option(PAMELA_OFFER_OPTION, $clean);
189+
190+
wp_safe_redirect(add_query_arg(
191+
['page' => 'pamela-offer-redirect', 'updated' => '1'],
192+
admin_url('options-general.php')
193+
));
194+
exit;
195+
}
196+
197+
function pamela_offer_render_admin_page() {
198+
if (!current_user_can('manage_options')) { return; }
199+
200+
$offers = pamela_offer_get_offers();
201+
// pad with blank rows so admin can add new entries
202+
$blank = ['slug' => '', 'start' => '', 'end' => '', 'redirect_to' => ''];
203+
$rows = array_merge($offers, [$blank, $blank, $blank]);
204+
205+
$tz = wp_timezone_string();
206+
?>
207+
<div class="wrap">
208+
<h1>Offer Redirects</h1>
209+
210+
<?php if (!empty($_GET['updated'])): ?>
211+
<div class="notice notice-success is-dismissible"><p>Saved.</p></div>
212+
<?php endif; ?>
213+
214+
<div class="card" style="max-width:900px;padding:1em 1.5em;">
215+
<h2>How it works</h2>
216+
<ol>
217+
<li>One row per offer page. Slug = the page slug (no slashes), e.g. <code>reiki-professional-bundle-final</code>.</li>
218+
<li><strong>Start / End</strong> format: <code>YYYY-MM-DD HH:MM:SS</code>. Site timezone is <code><?php echo esc_html($tz); ?></code>.</li>
219+
<li>When a visitor hits the slug page <em>outside</em> the start→end window, they get 302-redirected to <strong>Redirect To</strong>.</li>
220+
<li><em>Inside</em> the window = no redirect, the page shows normally.</li>
221+
<li>Leave a row completely blank to ignore it. Incomplete rows are dropped on save.</li>
222+
</ol>
223+
<h3>Diagnostics</h3>
224+
<ul style="list-style:disc;padding-left:1.5em;">
225+
<li>Append <code>?offer_debug=1</code> to any offer URL to see the decision trace instead of redirecting.</li>
226+
<li>Response headers on every front-end request: <code>X-Pamela-Offer-Redirect: loaded</code> (plugin active) and <code>X-Pamela-Offer-Fired: yes</code> (template_redirect ran — i.e. cache did not serve a stale copy).</li>
227+
<li>If <code>X-Pamela-Offer-Fired</code> is missing but the plugin is active, Kinsta/CDN page cache is serving the URL — add it to the cache-exclusion list and purge.</li>
228+
</ul>
229+
</div>
230+
231+
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="margin-top:1.5em;">
232+
<input type="hidden" name="action" value="pamela_offer_save" />
233+
<?php wp_nonce_field('pamela_offer_save'); ?>
234+
235+
<table class="widefat striped" style="max-width:1100px;">
236+
<thead>
237+
<tr>
238+
<th style="width:26%;">Page slug</th>
239+
<th style="width:20%;">Start (<?php echo esc_html($tz); ?>)</th>
240+
<th style="width:20%;">End (<?php echo esc_html($tz); ?>)</th>
241+
<th style="width:26%;">Redirect to (slug)</th>
242+
</tr>
243+
</thead>
244+
<tbody>
245+
<?php foreach ($rows as $i => $row): ?>
246+
<tr>
247+
<td>
248+
<input type="text" name="offers[<?php echo $i; ?>][slug]"
249+
value="<?php echo esc_attr($row['slug']); ?>"
250+
class="regular-text" placeholder="offer-page-slug" />
251+
</td>
252+
<td>
253+
<input type="text" name="offers[<?php echo $i; ?>][start]"
254+
value="<?php echo esc_attr($row['start']); ?>"
255+
class="regular-text" placeholder="2026-04-23 00:00:00" />
256+
</td>
257+
<td>
258+
<input type="text" name="offers[<?php echo $i; ?>][end]"
259+
value="<?php echo esc_attr($row['end']); ?>"
260+
class="regular-text" placeholder="2026-04-24 23:59:59" />
261+
</td>
262+
<td>
263+
<input type="text" name="offers[<?php echo $i; ?>][redirect_to]"
264+
value="<?php echo esc_attr($row['redirect_to']); ?>"
265+
class="regular-text" placeholder="destination-slug" />
266+
</td>
267+
</tr>
268+
<?php endforeach; ?>
269+
</tbody>
270+
</table>
271+
272+
<p style="margin-top:1em;">
273+
<button type="submit" class="button button-primary">Update offers</button>
274+
<span style="margin-left:1em;color:#666;">Need more rows? Save first — three blank rows are added every time this page loads.</span>
275+
</p>
276+
</form>
277+
</div>
278+
<?php
279+
}

0 commit comments

Comments
 (0)