chore: initial commit — Buddy v0.1.0 (Phase A complete)
Buddy is born. First commit of a new standalone WordPress plugin — the spiritual successor to the tamagotchi that once lived inside A-WP-Notes v1.1.5 (gracefully retired). Rebuilt from scratch with all the v3-discipline lessons baked in from day one. PHASE A — pet exists - Dashboard widget at WP Admin → Dashboard showing SVG character + name + mood label + four stats bars. - Dedicated admin page at WP Admin → Buddy → My Buddy (bigger view). - About page with side-by-side intro + plain-prose cards (Logbook About-page pattern carried forward). - Settings page with name-rename form + Updates panel. - Per-user state in user_meta key buddy_state (each WP admin gets their own pet, no shared state). - Inline SVG sprite renderer with three mood tones (happy/neutral/ sad) and three sizes (sm/md/lg). CSS keyframe animations: bobbing + periodic blinking. Zero image files. - Self-hosted update checker wired up from commit 1, ported from Logbook v3.3.5: /releases/latest with /tags?limit=1 fallback, 12h success cache / 1h negative cache. UI on Settings page. - dashicons-pets admin-menu icon — literal paw-print, brand match. ARCHITECTURE LOCKED FROM COMMIT 1 - Single-word brand name "Buddy" — no WP prefix, no future rebrand. - Public GPL v2+ Gitea repo (ranger/a-buddy). - Constants prefix BUDDY_*, function prefix buddy_*, text domain buddy. Clean naming throughout — none of Logbook's wp-notes-* historical-artifact baggage. - Single H1 per admin page, no nested toggle boxes, no duplicate sections — Tier-1 discipline carried forward from Logbook. - All assets local (inline SVG, plain CSS), no third-party CDN, no Gravatar-style external pings. NOT IN THIS RELEASE (planned) - Phase B — Feed/Play/Clean/Sleep interactions + cooldown timers. - Phase C — WP-cron decay + "Buddy is hungry" dismissible notices (port the persistent-dismissal pattern from Logbook). - Phase D — Multiple species (dog, dragon, sprite), per-species personality phrases. - Phase E — Site-health hook: pet stats react to wp_get_site_health() results. The killer feature. - Phase F — Pro tier (€2.99 lifetime) with custom skins + multi-pet. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+113
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* Buddy state — persistence for the pet's stats and identity.
|
||||
*
|
||||
* Storage: per-user, in `wp_usermeta` under key `buddy_state`. A single
|
||||
* JSON-shaped associative array. Picked user_meta over site-wide
|
||||
* options so multiple admins on the same site each get their own pet,
|
||||
* matching the per-user mental model.
|
||||
*
|
||||
* Stats range 0–100. Higher = better. Decay happens via WP-cron later
|
||||
* (Phase C); for Phase A the stats just persist as last set.
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) { exit; }
|
||||
|
||||
const BUDDY_META_KEY = 'buddy_state';
|
||||
|
||||
/**
|
||||
* Default state for a freshly-adopted pet. Used the first time a user
|
||||
* visits a Buddy-rendering page.
|
||||
*/
|
||||
function buddy_default_state() {
|
||||
return array(
|
||||
'name' => __( 'Buddy', 'buddy' ),
|
||||
'species' => 'default',
|
||||
'hunger' => 80,
|
||||
'happiness' => 80,
|
||||
'health' => 90,
|
||||
'energy' => 70,
|
||||
'born_at' => time(),
|
||||
'last_tick' => time(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the current user's Buddy state, falling back to defaults and
|
||||
* persisting them on first read so the pet has a "birthday" timestamp.
|
||||
*
|
||||
* @param int $user_id Optional. Defaults to current user.
|
||||
* @return array { name, species, hunger, happiness, health, energy, born_at, last_tick }
|
||||
*/
|
||||
function buddy_get_state( $user_id = 0 ) {
|
||||
$user_id = $user_id ? (int) $user_id : get_current_user_id();
|
||||
if ( ! $user_id ) { return buddy_default_state(); }
|
||||
|
||||
$state = get_user_meta( $user_id, BUDDY_META_KEY, true );
|
||||
if ( ! is_array( $state ) || empty( $state ) ) {
|
||||
$state = buddy_default_state();
|
||||
update_user_meta( $user_id, BUDDY_META_KEY, $state );
|
||||
}
|
||||
|
||||
// Merge against defaults so newly-added keys appear with sensible
|
||||
// values for users adopted under earlier versions.
|
||||
return array_merge( buddy_default_state(), $state );
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a partial state update for the current user. Unknown keys
|
||||
* are silently dropped; values are clamped to valid ranges.
|
||||
*
|
||||
* @param array $patch Keys: name (str), species (str), hunger/happiness/health/energy (int 0–100).
|
||||
* @return array The full updated state.
|
||||
*/
|
||||
function buddy_update_state( array $patch, $user_id = 0 ) {
|
||||
$user_id = $user_id ? (int) $user_id : get_current_user_id();
|
||||
if ( ! $user_id ) { return buddy_default_state(); }
|
||||
|
||||
$state = buddy_get_state( $user_id );
|
||||
|
||||
$stat_keys = array( 'hunger', 'happiness', 'health', 'energy' );
|
||||
foreach ( $stat_keys as $k ) {
|
||||
if ( array_key_exists( $k, $patch ) ) {
|
||||
$state[ $k ] = max( 0, min( 100, (int) $patch[ $k ] ) );
|
||||
}
|
||||
}
|
||||
if ( array_key_exists( 'name', $patch ) ) {
|
||||
$name = sanitize_text_field( (string) $patch['name'] );
|
||||
if ( $name !== '' && mb_strlen( $name ) <= 32 ) {
|
||||
$state['name'] = $name;
|
||||
}
|
||||
}
|
||||
if ( array_key_exists( 'species', $patch ) ) {
|
||||
$allowed_species = array( 'default' ); // Phase D will widen this.
|
||||
$sp = sanitize_key( (string) $patch['species'] );
|
||||
if ( in_array( $sp, $allowed_species, true ) ) {
|
||||
$state['species'] = $sp;
|
||||
}
|
||||
}
|
||||
|
||||
update_user_meta( $user_id, BUDDY_META_KEY, $state );
|
||||
return $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Average of the four stats — used as a single "mood" indicator for
|
||||
* the dashboard widget. Returns 0–100.
|
||||
*/
|
||||
function buddy_overall_mood( array $state ) {
|
||||
$sum = (int) $state['hunger'] + (int) $state['happiness'] + (int) $state['health'] + (int) $state['energy'];
|
||||
return (int) round( $sum / 4 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a one-word emoji / status label based on overall mood. Pure
|
||||
* cosmetic, used in the dashboard widget header.
|
||||
*/
|
||||
function buddy_mood_label( $mood_score ) {
|
||||
if ( $mood_score >= 80 ) { return array( 'label' => __( 'Thriving', 'buddy' ), 'tone' => 'happy' ); }
|
||||
if ( $mood_score >= 60 ) { return array( 'label' => __( 'Content', 'buddy' ), 'tone' => 'happy' ); }
|
||||
if ( $mood_score >= 40 ) { return array( 'label' => __( 'Okay', 'buddy' ), 'tone' => 'neutral' ); }
|
||||
if ( $mood_score >= 20 ) { return array( 'label' => __( 'Hungry', 'buddy' ), 'tone' => 'sad' ); }
|
||||
return array( 'label' => __( 'Distressed', 'buddy' ), 'tone' => 'sad' );
|
||||
}
|
||||
Reference in New Issue
Block a user