is_main_query() && $query->is_search()) { // Deine Custom Post Types ausschließen $excluded = ['adventurebook', 'adventurebook_page']; // Falls es bereits post_type gesetzt ist, nur ergänzen if (!empty($query->get('post_type'))) { $post_types = (array)$query->get('post_type'); $post_types = array_diff($post_types, $excluded); $query->set('post_type', $post_types); } else { // Standardmäßig auf 'post' und 'page' beschränken $query->set('post_type', ['post', 'page']); } } }); /** * Safely send HTML content in a wp_send_json_error or wp_send_json_success call. * * @param string $message The message string, can contain HTML tags. * @param array $allowed_html Optional. Allowed HTML tags. Defaults to common post tags. * @param bool $success Optional. True = wp_send_json_success, False = wp_send_json_error. */ function adventurebook_send_json_message($message, $allowed_html = null, $success = false) { // Default allowed tags if none provided if ($allowed_html === null) { $allowed_html = array( 'p' => array( 'style' => array(), 'class' => array(), ), 'a' => array( 'href' => array(), 'title' => array(), 'target' => array(), ), 'strong' => array(), 'em' => array(), 'br' => array(), ); } // Escape URLs in tags inside the message $message = wp_kses($message, $allowed_html); // Send the JSON response if ($success) { wp_send_json_success($message); } else { wp_send_json_error($message); } } /** * checks if a character can travel from one zone to another * on the from_zone page, a decision has to exist which leads to the to_zone page * if the decision has a restriction, the character must fulfill the restriction to travel * * @param $character_id * @param $from_zone_id * @param $to_zone_id * * @return bool true if the character can travel, false otherwise */ function adventurebook_can_travel_from_zone_to_zone($character_id, $from_zone_id, $to_zone_id): bool { global $adventurebook_id; // if the character_id is 0, the character is not logged in or this is a story without game elements and restrictions if ($character_id == 0) { $decisions = get_post_meta($from_zone_id, '_adventurebook_decision_options', true); $decisions = is_array($decisions) ? $decisions : array(); foreach ($decisions as $decision) { if ($decision['target'] == $to_zone_id) { return true; } } // if the destination page is the starting page, we return true because jumping to the start is always possible $start_page_id = get_post_meta($adventurebook_id, '_adventurebook_start_page', true); if ($to_zone_id == $start_page_id) { return true; } return false; } // get the current character record $adventurebook_current_character = Character::get_character_by_id($character_id); $character_zone = $adventurebook_current_character->location_id; $decisions = get_post_meta($from_zone_id, '_adventurebook_decision_options', true); $decisions = is_array($decisions) ? $decisions : array(); foreach ($decisions as $decision) { // valid decision? if (!$decision) { continue; } if ($decision['target'] == $to_zone_id) { // check if there is a restriction for this decision if (isset($decision['restriction_type']) && isset($decision['restriction_value']) && $decision['restriction_type'] != 'none' && $decision['restriction_value'] != '' ) { $restriction_type = $decision['restriction_type']; $restriction_value = $decision['restriction_value']; // check if the character has the restriction // restriction type: no_enemies if ($restriction_type == 'no_enemies') { $can_travel = true; // check if there are any alive enemies in the character's current zone $enemies_in_zone = Enemy::get_enemies_for_location_id($adventurebook_current_character->location_id, $character_id); foreach ($enemies_in_zone as $enemy) { if ($enemy->is_alive()) { $can_travel = false; break; } } return $can_travel; } // restriction type: item if ($restriction_type == 'item') { if ($adventurebook_current_character->has_item_with_code($restriction_value)) { return true; } return false; } // restriction type: game_var if ($restriction_type == 'game_var') { // the restriction value is in the form game_var_name=expected_value or game_var_nameexpected_value if (preg_match('/^([a-zA-Z0-9_]+)([=<>]{1})(.+)$/', $restriction_value, $matches)) { $var_name = $matches[1]; $operator = $matches[2]; $expected_value = $matches[3]; $actual_value = $adventurebook_current_character->get_game_var($var_name); if ($operator == '=' && strval($actual_value) == $expected_value) { return true; } if ($operator == '<' && floatval($actual_value) < floatval($expected_value)) { return true; } if ($operator == '>' && floatval($actual_value) > floatval($expected_value)) { return true; } return false; } else { // invalid restriction value format return false; } } // restriction type: stat if ($restriction_type == 'stat') { // the restriction value is in the form stat_name=expected_value or stat_nameexpected_value if (preg_match('/^([a-zA-Z0-9_ ]+)([=<>]{1})(.+)$/', $restriction_value, $matches)) { $stat_name = $matches[1]; $operator = $matches[2]; $expected_value = $matches[3]; $stats = $adventurebook_current_character->get_stats(); $actual_value = $stats[$stat_name] ?? 0; if ($operator == '=' && strval($actual_value) == $expected_value) { return true; } if ($operator == '<' && floatval($actual_value) < floatval($expected_value)) { return true; } if ($operator == '>' && floatval($actual_value) > floatval($expected_value)) { return true; } return false; } else { // invalid restriction value format return false; } } // restriction type: skill if ($restriction_type == 'skill') { // the restriction value is in the form skill_name=expected_value or skill_nameexpected_value if (preg_match('/^([a-zA-Z0-9_ ]+)([=<>]{1})(.+)$/', $restriction_value, $matches)) { $skill_name = $matches[1]; $operator = $matches[2]; $expected_value = $matches[3]; $actual_value = $adventurebook_current_character->get_skill_value($skill_name); if ($operator == '=' && strval($actual_value) == $expected_value) { return true; } if ($operator == '<' && floatval($actual_value) < floatval($expected_value)) { return true; } if ($operator == '>' && floatval($actual_value) > floatval($expected_value)) { return true; } return false; } else { // invalid restriction value format return false; } } } } } return true; } /** * checks if the given adventurebook_id is a game * * @param $adventurebook_id * * @return bool */ function adventurebook_is_game($adventurebook_id): bool { // the adventure book id is the parent of the page $adventurebook_type = get_post_meta($adventurebook_id, '_adventurebook_type', true); if ($adventurebook_type === 'game') { return true; } else { return false; } } function adventurebook_check_ownership($character_id) { // check if the character is owned by the actual user if ($character_id == 0) { $response = array( 'status' => 'no character id provided', ); wp_send_json($response); } $user_id = get_current_user_id(); $character = Character::get_character_by_id($character_id); if ($character->user_id != $user_id) { $response = array( 'status' => 'character not owned by user', ); wp_send_json($response); } } function adventurebook_drop_item_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } // get the current character id if (isset($_POST['character_id'])) { $character_id = intval($_POST['character_id']); } else { $character_id = 0; } adventurebook_check_ownership($character_id); // get the character $character = Character::get_character_by_id($character_id); // check if the required parameters are set if (isset($_POST['item_id'])) { $item_id = sanitize_text_field(wp_unslash($_POST['item_id'])); // remove the item from the character's inventory $character->remove_item_from_inventory($item_id); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_drop_item', 'adventurebook_drop_item_ajax'); function adventurebook_equip_item_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } // get the current character id if (isset($_POST['character_id'])) { $character_id = intval($_POST['character_id']); } else { $character_id = 0; } adventurebook_check_ownership($character_id); // get the character $character = Character::get_character_by_id($character_id); // check if the required parameters are set if (isset($_POST['item_id'])) { $item_id = intval(sanitize_text_field(wp_unslash($_POST['item_id']))); // equip the item to the character's inventory $character->equip_item($item_id); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); //wp_send_json( $response ); } add_action('wp_ajax_adventurebook_equip_item', 'adventurebook_equip_item_ajax'); function adventurebook_unequip_item_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren // get the current character id if (isset($_POST['character_id'])) { $character_id = intval($_POST['character_id']); } else { $character_id = 0; } adventurebook_check_ownership($character_id); // get the character $character = Character::get_character_by_id($character_id); // check if the required parameters are set if (isset($_POST['slotname'])) { $slotname = sanitize_text_field(wp_unslash($_POST['slotname'])); // just unequip this slot $character->unequip_slot($slotname); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_unequip_item', 'adventurebook_unequip_item_ajax'); function adventurebook_use_item_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren // get the current character id if (isset($_POST['character_id'])) { $character_id = intval($_POST['character_id']); } else { $character_id = 0; } // get the character $character = Character::get_character_by_id($character_id); $character->stop_working(); adventurebook_check_ownership($character_id); // check if the required parameters are set if (isset($_POST['item_id'])) { $item_id = ab_get('item_id'); // use the item from the character's inventory $output = $character->use_item($item_id); foreach ($output as $line) { // only add the line if it does not contain a directive (e.g. [preserve_charges]) // directives are in [ ] if (preg_match('/^\[.*\]$/', trim($line))) { continue; } add_transient_message($line); } } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_use_item', 'adventurebook_use_item_ajax'); /** * AJAX handler to take an item from the page * this will remove the item from the game, but add it's code to the character's inventory with a new unique inventory id * and an amount ("count") of 1. */ function adventurebook_take_item_ajax(): void { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } if (isset($_POST['character_id'])) { $character_id = intval($_POST['character_id']); } else { $character_id = 0; } adventurebook_check_ownership($character_id); // check if the required parameters are set if (isset($_POST['item_id'])) { $item_id = intval(ab_get('item_id')); $item = Item::get_item_by_id($item_id); $character = Character::get_character_by_id($character_id); // check if this item belongs to the character's current zone if ($item->character_id == $character_id && $item->location_id == $character->location_id) { // set the associated spawn's last_spawned to the current time so that the respawn timer starts $item_spawn = Item_Spawn::get_item_spawn_by_id($item->spawn_id); if ($item_spawn) { $item_spawn->update_last_spawned(); } // if the item's code is "money", add the "Charges" stat to the character's wealth and delete the item if ($item->code == "money") { $stats = $item->get_complete_stats(); if (isset($stats['Charges'])) { $charges = intval($stats['Charges']); $character->add_wealth($charges); // delete the item Item::delete_item($item_id); // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); return; } } // add the item to the character's inventory $character->pickup_item($item->id); } } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_take_item', 'adventurebook_take_item_ajax'); function adventurebook_buy_item_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } // check if the required parameters are set if (isset($_POST['item_id']) && isset($_POST['page_id']) && isset($_POST['character_id'])) { $item_id = intval($_POST['item_id']); $page_id = intval($_POST['page_id']); $character_id = intval($_POST['character_id']); adventurebook_check_ownership($character_id); // check if the item is for sale on this page $shop_items_codes = get_post_meta($page_id, '_adventurebook_shop_items', true); $shop_items_codes = is_array($shop_items_codes) ? $shop_items_codes : explode(',', $shop_items_codes); $shop_items_codes = array_map('trim', $shop_items_codes); $shop_items_codes = array_filter($shop_items_codes); $item_definition = Item_Definition::get_item_definition_by_id($item_id); if (!$item_definition || !in_array($item_definition->code, $shop_items_codes)) { wp_send_json_error(esc_html(__('This item is not for sale on this page.', 'adventurebook'))); } // get the wealth stat for the character $character = Character::get_character_by_id($character_id); if (!$character) { wp_send_json_error(esc_html(__('Character not found.', 'adventurebook'))); } $adventurebook_combat_system = adventurebook_get_combat_system_for_adventurebook($adventurebook_id); if (!$adventurebook_combat_system) { wp_send_json_error(esc_html(__('Combat system not found.', 'adventurebook'))); } $complete_wealth = $character->get_wealth(); // get the item price $item_price = $item_definition->get_base_price(); $shop_price_factor = get_post_meta($page_id, '_adventurebook_shop_price_factor', true); $shop_price_factor = floatval($shop_price_factor); if ($shop_price_factor <= 0) { $shop_price_factor = 1.0; } $item_price = intval($item_price * $shop_price_factor); // check if the character has enough wealth to buy the item if ($complete_wealth < $item_price) { status_header(403); $unit = $adventurebook_combat_system->get_wealth_unit(); /* translators: %s is the unit of wealth, e.g. "gold" */ wp_send_json_error(sprintf(esc_html(__('You do not have enough %s to buy this item.', 'adventurebook')), esc_html($unit))); } // deduct the item price from the character's wealth $character->remove_wealth($item_price); // create a new item instance in the character's inventory $new_item = Item::create_item($character_id, $item_definition->code, $character->location_id, null, 1); $character->pickup_item($new_item->id); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_buy_item', 'adventurebook_buy_item_ajax'); function adventurebook_sell_item_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } $adventurebook_combat_system = adventurebook_get_combat_system_for_adventurebook($adventurebook_id); if (!$adventurebook_combat_system) { wp_send_json_error(esc_html(__('Combat system not found.', 'adventurebook'))); } // check if the required parameters are set if (isset($_POST['item_id']) && isset($_POST['page_id']) && isset($_POST['character_id'])) { $item_id = sanitize_text_field(wp_unslash($_POST['item_id'])); $page_id = intval($_POST['page_id']); $character_id = intval($_POST['character_id']); adventurebook_check_ownership($character_id); // check if this page is of type "Shop" // Gespeicherter Typ $page_type = get_post_meta($page_id, '_adventurebook_page_type', true); // Nur anzeigen, wenn Typ bereits "Shop" ist if ($page_type !== 'shop') { wp_send_json_error(esc_html(__('Here is no shop.', 'adventurebook'))); } $character = Character::get_character_by_id($character_id); if (!$character) { wp_send_json_error(esc_html(__('Character not found.', 'adventurebook'))); } // check if the item is in the character's inventory $item_count = $character->has_item_with_id($item_id); if (!$item_count) { wp_send_json_error(esc_html(__('This item is not in your inventory.', 'adventurebook'))); } $item = Item::get_item_by_id($item_id); // get the item price $stats = $item->get_complete_stats(); $item_price_stat_name = $adventurebook_combat_system->get_item_stat_name_for_price(); $item_price = $item->get_base_price(); // add the item price to the character's wealth $character->add_wealth($item_price); // delete the item, thus removing it from the character's inventory and from the game // if the item is a resource, we need to decrease the charges instead of deleting the item if ($item->is_resource()) { $item->decrease_charges(1); // if the charges are now 0, delete the item if ($item->get_charges() <= 0) { Item::delete_item($item_id); } } else { Item::delete_item($item_id); } $response = array( 'status' => 'success', ); } else { $response = array( 'status' => 'missing parameters', ); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_sell_item', 'adventurebook_sell_item_ajax'); function adventurebook_sell_all_items_ajax(): void { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } $adventurebook_combat_system = adventurebook_get_combat_system_for_adventurebook($adventurebook_id); if (!$adventurebook_combat_system) { wp_send_json_error(esc_html(__('Combat system not found.', 'adventurebook'))); } // check if the required parameters are set if (isset($_POST['item_id']) && isset($_POST['page_id']) && isset($_POST['character_id'])) { $item_id = sanitize_text_field(wp_unslash($_POST['item_id'])); $page_id = intval($_POST['page_id']); $character_id = intval($_POST['character_id']); $item = Item::get_item_by_id($item_id); adventurebook_check_ownership($character_id); // check if this page is of type "Shop" $page_type = get_post_meta($page_id, '_adventurebook_page_type', true); // Nur anzeigen, wenn Typ bereits "Shop" ist if ($page_type !== 'shop') { wp_send_json_error(esc_html(__('Here is no shop.', 'adventurebook'))); } $character = Character::get_character_by_id($character_id); if (!$character) { wp_send_json_error(esc_html(__('Character not found.', 'adventurebook'))); } // check if the item is in the character's inventory if($item->is_resource()) { $item_count = $item->get_charges(); } else { $item_count = $character->get_identical_item_count_by_id($item_id); } if (!$item_count) { wp_send_json_error(esc_html(__('This item is not in your inventory.', 'adventurebook'))); } // get the item price $item_price = $item->get_base_price() * $item_count; // add the item price to the character's wealth $character->add_wealth($item_price); $items = $character->get_identical_items_by_id($item_id); foreach ($items as $item) { // delete the item, thus removing it from the character's inventory and from the game Item::delete_item($item->id); } } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_sell_all_items', 'adventurebook_sell_all_items_ajax'); function adventurebook_use_skill_ajax(): void { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren $character_id = ab_get('character_id'); adventurebook_check_ownership($character_id); $character = Character::get_character_by_id($character_id); $character->stop_working(); $skill_name = ab_get('skill_name'); // use the skill $output = $character->get_combat_system()->combatant_use_skill($character, $skill_name); foreach ($output as $line) { add_transient_message($line); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_use_skill', 'adventurebook_use_skill_ajax'); /** * This function is called via AJAX when the player clicks on a link generated by the "showkeylink" script command. * It sets the current key of the script to the given key, so that the script can continue execution from that point. * The function expects the following parameters to be passed via POST: * - script_code: the code of the script to be executed * - key: the key to be set * - character_id: the id of the character * * @return void */ function adventurebook_set_key_for_script() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } // check if the required parameters are set if (isset($_POST['script_code']) && isset($_POST['key']) && isset($_POST['character_id'])) { $script_code = sanitize_text_field(wp_unslash($_POST['script_code'])); $key = sanitize_text_field(wp_unslash($_POST['key'])); $character_id = intval($_POST['character_id']); adventurebook_check_ownership($character_id); $character = Character::get_character_by_id($character_id); // set the current key of the script to the given key // this is done by setting a game var $character->set_game_var("script_" . $script_code . "_key", $key); // save the character $character->save_to_db(); $response = array( 'status' => 'success', ); } else { $response = array( 'status' => 'missing parameters', ); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_set_key_for_script', 'adventurebook_set_key_for_script'); function adventurebook_get_user_input_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } // check if the required parameters are set // var_name, input_data, character_id, adventurebook_id if (isset($_POST['var_name']) && isset($_POST['input_data']) && isset($_POST['character_id'])) { $var_name = ab_get('var_name'); $input_data = ab_get('input_data'); $character_id = ab_get('character_id'); adventurebook_check_ownership($character_id); $character = Character::get_character_by_id($character_id); // set the game var to the input data $character->set_game_var($var_name, $input_data); // save the character $character->save_to_db(); $response = array( 'status' => 'success', ); } else { $response = array( 'status' => 'missing parameters', ); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_get_user_input', 'adventurebook_get_user_input_ajax'); function adventurebook_delete_decision_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } // check if the required parameters are set if (isset($_POST['source_page_id']) && isset($_POST['destination_page_id'])) { $source_page_id = intval(ab_get('source_page_id')); $destination_page_id = intval(ab_get('destination_page_id')); // check if the current user is a administrator if (!current_user_can('manage_options')) { $response = array( 'status' => 'not authorized', ); wp_send_json($response); } // get the decision_options of the source page $decision_options = get_post_meta($source_page_id, '_adventurebook_decision_options', true); $decision_options = is_array($decision_options) ? $decision_options : array(); // find the decision that leads to the destination page foreach ($decision_options as $index => $decision) { if ($decision['target'] == $destination_page_id) { // remove this decision unset($decision_options[$index]); break; } } // update the decision_options of the source page update_post_meta($source_page_id, '_adventurebook_decision_options', $decision_options); $response = array( 'status' => 'success', ); } else { $response = array( 'status' => 'missing parameters', ); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_delete_decision', 'adventurebook_delete_decision_ajax'); function adventurebook_kill_enemy_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } // check if the required parameters are set if (isset($_POST['enemy_id']) && isset($_POST['character_id'])) { $enemy_id = intval(ab_get('enemy_id')); $character_id = intval(ab_get('character_id')); adventurebook_check_ownership($character_id); // if we are admin we can kill the enemy directly if (current_user_can('manage_options')) { $enemy = Enemy::get_enemy_by_id($enemy_id); if ($enemy) { // check if the combat system of the adventurebook has the method "kill_enemy" $adventurebook_combat_system = adventurebook_get_combat_system_for_adventurebook($adventurebook_id); if (method_exists($adventurebook_combat_system, 'kill_enemy')) { $adventurebook_combat_system->kill_enemy(Character::get_character_by_id($character_id), $enemy); } else { $spawn = Enemy_Spawn::get_enemy_spawn_by_id($enemy->spawn_id); $spawn->update_last_defeated(); Enemy::delete_enemy_by_id($enemy_id); } } } $response = array( 'status' => 'success', ); } else { $response = array( 'status' => 'missing parameters', ); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_kill_enemy', 'adventurebook_kill_enemy_ajax'); function adventurebook_attack_enemy_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } // check if the required parameters are set if (isset($_POST['enemy_id']) && isset($_POST['character_id'])) { $enemy_id = intval($_POST['enemy_id']); $character_id = intval($_POST['character_id']); adventurebook_check_ownership($character_id); $character = Character::get_character_by_id($character_id); $character->stop_working(); // this will set the next action of the character to "attack_".$enemy_id $character->set_next_action("attack_" . $enemy_id); // save the character $character->save_to_db(); // afterwards, we return success - the next round will be started by the frontend via calling the refresh $response = array( 'status' => 'success', ); } else { $response = array( 'status' => 'missing parameters', ); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_attack_enemy', 'adventurebook_attack_enemy_ajax'); function adventurebook_threaten_enemy_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } if (isset($_POST['enemy_id']) && isset($_POST['character_id'])) { $enemy_id = intval($_POST['enemy_id']); $character_id = intval($_POST['character_id']); adventurebook_check_ownership($character_id); $character = Character::get_character_by_id($character_id); $enemy = Enemy::get_enemy_by_id($enemy_id); if (!$enemy) { wp_send_json(array('status' => 'enemy not found')); } // Get combat system to calculate combat strength $combat_system = adventurebook_get_combat_system_for_adventurebook($adventurebook_id); // Calculate combat strengths $character_strength = 0; $enemy_strength = 0; if ($combat_system && method_exists($combat_system, 'get_combat_strength')) { $character_strength = $combat_system->get_combat_strength($character); $enemy_strength = $combat_system->get_combat_strength($enemy); } // Calculate success chance: 50% base + 5% per point difference $strength_difference = $character_strength - $enemy_strength; $success_chance = 50 + ($strength_difference * 5); // Clamp between 5% and 95% $success_chance = max(5, min(95, $success_chance)); // Roll for success $roll = wp_rand(1, 100); $success = $roll <= $success_chance; if (!$success) { add_transient_message(esc_html(__('Your threat failed! The enemy stands its ground.', 'adventurebook'))); adventurebook_get_page_content_ajax(); return; } // If successful, find valid targets $current_location = $character->location_id; $decisions = get_post_meta($current_location, '_adventurebook_decision_options', true); $decisions = is_array($decisions) ? $decisions : array(); $valid_targets = array(); foreach ($decisions as $decision) { $has_restriction = isset($decision['restriction_type']) && isset($decision['restriction_value']) && $decision['restriction_type'] != 'none' && $decision['restriction_value'] != ''; if (!$has_restriction && isset($decision['target'])) { $valid_targets[] = intval($decision['target']); } } if (!empty($valid_targets)) { $target_page = $valid_targets[array_rand($valid_targets)]; $enemy->location_id = $target_page; $enemy->save_to_db(); add_transient_message(esc_html(__('You successfully threatened the enemy! It fled to another location.', 'adventurebook'))); } else { add_transient_message(esc_html(__('The enemy has nowhere to flee!', 'adventurebook'))); } } adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_threaten_enemy', 'adventurebook_threaten_enemy_ajax'); function adventurebook_respawn_ajax() { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } else { // json error $response = array( 'status' => 'missing adventurebook_id', ); wp_send_json($response); return; } // get the current character id if (isset($_POST['character_id'])) { $character_id = intval($_POST['character_id']); } else { // json error $response = array( 'status' => 'missing character_id', ); wp_send_json($response); return; } adventurebook_check_ownership($character_id); // get the character $character = Character::get_character_by_id($character_id); if ($character) { $character->stop_working(); // respawn the character at the start page of this adventurebook // get the start page $start_page_id = get_post_meta($adventurebook_id, '_adventurebook_start_page', true); $character->location_id = intval($character->get_stat('RespawnZone')) ? intval($character->get_stat('RespawnZone')) : $start_page_id; // clear the next action $character->set_next_action(""); // respawn the character $character->respawn(); $response = array( 'status' => 'success', ); } else { $response = array( 'status' => 'character not found', ); } // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_respawn', 'adventurebook_respawn_ajax'); /** * AJAX handler to get the content of an adventurebook page * This function checks if the user is logged in (if the adventurebook is a game) * and if the user has a current character selected. * It then fetches the current page of the adventurebook and returns the HTML content. * * IMPORTANT * * Each call of this function will calculate a new round for the character if the adventurebook is a game. */ function adventurebook_get_page_content_ajax() { global $adventurebook_is_game, $page_id, $adventurebook_combat_system, $adventurebook_current_character, $adventurebook_id, $adventurebook_current_character_id, $decisions, $adventurebook_character_management_url; check_ajax_referer('adventurebook_ajax_nonce'); $error_msg = ''; // the adventurebook_id was passed to the ajax function if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } $startpage = get_post_meta($adventurebook_id, '_adventurebook_start_page', true); // check if the startpage is set if (!$startpage) { wp_send_json_error('

' . esc_html(__('No start page configured for this Adventurebook.', 'adventurebook')) . '

'); } $adventurebook_is_game = adventurebook_is_game($adventurebook_id); // for adventurebook type "game" only: check if the character management page is configured if ($adventurebook_is_game) { $character_page_id = get_option('adventurebook_character_page_id', 0); if (!$character_page_id) { wp_send_json_error('

' . esc_html(__('Character management page not configured.', 'adventurebook')) . '

'); } $adventurebook_character_management_url = esc_url(get_permalink($character_page_id)); // Is the user logged in? You need to be logged in to use the Adventurebook // else we can't assign a character to the user if (!is_user_logged_in()) { $login_url = wp_login_url(); /* translators: %s is the login url */ wp_send_json_error('

' . sprintf(__('You need to log in to access this page.', 'adventurebook'), esc_url($login_url)) . '

'); } global $wpdb; $user_id = get_current_user_id(); // Get the current character of the user $adventurebook_current_character_id = get_user_meta($user_id, 'adventurebook_current_character_id_' . $adventurebook_id, true); if (!$adventurebook_current_character_id) { // No current character selected /* translators: %s is the character management url */ adventurebook_send_json_message(sprintf(__('You have not selected a current character. Go to character management.', 'adventurebook'), esc_url($adventurebook_character_management_url))); } // Get the current character from the database $adventurebook_current_character = Character::get_character_by_id($adventurebook_current_character_id); if (!$adventurebook_current_character) { // If the current character is not found, reset the meta delete_user_meta($user_id, 'adventurebook_current_character_id_' . $adventurebook_id); /* translators: %s is the character management url */ wp_send_json_error('

' . sprintf(__('The current character no longer exists. Go to character management.', 'adventurebook'), $adventurebook_character_management_url ) . '

'); } adventurebook_check_ownership($adventurebook_current_character_id); } // if there is no adventurebook_id, return an error message if (!$adventurebook_id) { wp_send_json_error('

' . esc_html(__('No Adventurebook ID provided.', 'adventurebook')) . '

'); } // fetch the Adventurebook $adventurebook = get_post($adventurebook_id); // fetch the combat system $adventurebook_combat_system = adventurebook_get_combat_system_for_adventurebook($adventurebook_id); // Check if the Adventurebook exists and is of the type `adventurebook` if (!$adventurebook || $adventurebook->post_type !== 'adventurebook') { wp_send_json_error('

' . esc_html(__('Adventurebook not found.', 'adventurebook')) . '

'); } $page_id = get_actual_page_id($adventurebook_id); $adventurebook_page = get_post($page_id); // Check if the Adventurebook page exists and is of the type `adventurebook_page` if (!$adventurebook_page || $adventurebook_page->post_type !== 'adventurebook_page') { wp_send_json_error('

' . esc_html(__('Adventurebook page not found.', 'adventurebook')) . '

'); } // Get the decision options for the current page $decisions = get_post_meta($page_id, '_adventurebook_decision_options', true); $decisions = is_array($decisions) ? $decisions : array(); // set the current page as the current zone for the character, if this is a game // else set the page id in the session if ($adventurebook_is_game) { $wpdb->update($wpdb->prefix . "adventurebook_characters", array('location_id' => $page_id), array('id' => $adventurebook_current_character_id));// phpcs:ignore WordPress.DB.DirectDatabaseQuery } else { // set the adventurebook_id AND the page_id in the session adventurebook_save_to_session('adventurebook_page_id_' . $adventurebook_id, $page_id); } // by default, we want to show the decisions (a script or s.th. else can change this) $show_decisions = true; if ($adventurebook_is_game) { // we arrived at a new page. If this is a shop page OR the last page was a shop page, we need to // invalidate the inventory cache for this character so that the "Sell" Buttons will be shown/hidden correctly $last_page_id = intval($adventurebook_current_character->get_game_var('last_page_id')); $last_page_type = get_post_meta($last_page_id, '_adventurebook_page_type', true); $current_page_type = get_post_meta($page_id, '_adventurebook_page_type', true); if ($last_page_type === 'shop' || $current_page_type === 'shop') { adventurebook_invalidate_inventory_cache($adventurebook_current_character_id); } // save the current page as last page $adventurebook_current_character->set_game_var('last_page_id', $page_id); // add 1 to the total rounds played $turns_played = intval($adventurebook_current_character->get_game_var('turns_played')); $turns_played++; $adventurebook_current_character->set_game_var('turns_played', $turns_played); // reduce the cooldown of all skills by 1 $adventurebook_current_character->reduce_skill_cooldowns(); // check for respawned items on this page Item_Spawn::check_for_respawns($adventurebook_current_character_id); // check for respawned enemies on this page Enemy_Spawn::check_for_respawns($adventurebook_current_character_id); // check for an attached script on this page and run it // save the location - if the script changes it, we need to reload the page after the script execution $current_char_location = $adventurebook_current_character->location_id; $script_code = get_post_meta($page_id, '_adventurebook_page_script', true); $script = Script::get_script_by_code($script_code); if ($script) { $script_output = $script->run_script($adventurebook_current_character); // if the script has set show_decisions to false, we need to respect this $show_decisions = $script->show_decisions; } else { $script_output = []; } // check if the location has changed - if so, we need to reload the page $adventurebook_current_character = Character::get_character_by_id($adventurebook_current_character_id); if ($adventurebook_current_character->location_id != $current_char_location) { adventurebook_get_page_content_ajax(); } // each page refresh means a new round - so we call the central doCombat method of the combat system // for this we need all enemies on this page for this character $enemies = Enemy::get_enemies_for_page($page_id, $adventurebook_current_character_id); $output = $adventurebook_combat_system->do_combat($adventurebook_current_character, $enemies); // TODO: check if the combat modifications (e.g. health) are saved to the character and enemies and are shown correctly below } // --------------------------- // // Start output // // --------------------------- $content = ''; // --------------------------- // // Audio Output // // --------------------------- $audio_id = get_post_meta($page_id, '_adventurebook_audio_id', true); if ($audio_id) { $audio_url = wp_get_attachment_url($audio_id); $content .= ''; } $content .= '
'; // --------------------------- // // Topbar Output // // --------------------------- if ($adventurebook_is_game) { $content .= adventurebook_get_character_topbar_output(); } // render the character card first so it floats to the top-right, // with the title and page content flowing along its left side if ($adventurebook_is_game) { $content .= adventurebook_get_character_output(); } $content .= '

' . $adventurebook_page->post_title; // show the page id for admins if the option is enabled if (current_user_can('manage_options') && get_option('adventurebook_enable_inline_editing', '0') === '1') { $content .= ' (ID: ' . esc_html($adventurebook_page->ID) . ''; // show an edit link for the page in the admin area $edit_url = get_edit_post_link($adventurebook_page->ID); $content .= ' | ' . esc_html(__('Edit Page', 'adventurebook')) . ''; $content .= ')'; } $content .= '

'; $content .= '
'; $content .= '
'; // --------------------------- // // Image Output // // --------------------------- // add the custom image and the page content $image_id = get_post_meta($page_id, '_additional_image', true); if ($image_id) { // fetch the image id $image_id = get_post_meta($page_id, '_additional_image', true); // fetch the image URL $thumb_url = wp_get_attachment_image_url($image_id, 'medium'); // Large version $large_url = wp_get_attachment_image_url($image_id, 'large'); // output the image if ($thumb_url) { $content .= ''; } } $content .= '
'; // --------------------------- // // Content Output // // --------------------------- $content .= apply_filters('the_content', $adventurebook_page->post_content); // --------------------------- // // Error + Transient Messages Output // // --------------------------- $content .= '
' . esc_html($error_msg) . '
'; // show any transient messages (e.g. from buying/selling items/using skills) // messages ending with ! will be shown in red $messages = get_transient('adventurebook_messages'); if ($messages && is_array($messages)) { // remove duplicates $messages = array_unique($messages); foreach ($messages as $message) { $message = trim($message); if (!empty($message)) { // if the message contains a |, split it and show the first part as message and the second part as text color $parts = explode('|', $message); if (count($parts) == 2) { $msg_text = trim($parts[0]); $msg_color = trim($parts[1]); $content .= '
' . wp_kses_post($msg_text) . '
'; } else { // check if the message ends with ! if (str_ends_with($message, '!')) { $content .= '
' . wp_kses_post($message) . '
'; } else { $content .= '
' . wp_kses_post($message) . '
'; } } } } } delete_transient('adventurebook_messages'); // --------------------------- // // Script Output // // --------------------------- // add the script output here, so that it is shown directly below the page content $formatted_script_output = []; // add any script output // the script output is an array, so wrap each line in a div if (isset($script_output) && is_array($script_output)) { foreach ($script_output as $line) { $line = trim($line); if (!empty($line)) { // allow a, button, div, onclick, strong, em, br, span with class // to allow links, bold, italic, line breaks and spans with classes for JS // e.g. for showkeylink // escape everything else $formatted_script_output[] = '
' . wp_kses($line, array( 'a' => array( 'href' => array(), 'onclick' => array(), 'target' => array(), 'rel' => array(), ), 'input' => array( 'onclick' => array(), 'class' => array(), 'type' => array(), 'value' => array(), 'placeholder' => array(), 'id' => array(), ), 'button' => array( 'onclick' => array(), 'class' => array(), 'type' => array(), ), 'font' => array('color' => array()), 'div' => array('class' => array()), 'span' => array('class' => array()), 'strong' => array(), 'em' => array(), 'br' => array(), ) ) . '
'; } } } if (!empty($formatted_script_output)) { $content .= '
'; $content .= implode('', $formatted_script_output); $content .= '
'; } $content .= '
'; if ($fixed_height) { $content .= '
--- ↓ scroll ---
'; } // --------------------------- // // Harvesting Output // // --------------------------- $content .= adventurebook_get_harvesting_output(); // --------------------------- // // You are dead! Output // // --------------------------- if ($adventurebook_is_game) { // check if the character is dead if (!$adventurebook_current_character->is_alive()) { // show the combat output so that the player can see what killed him $content .= adventurebook_show_combat_log($output); // show a message with the option to respawn at the start page $content .= '
'; $content .= '

' . esc_html(__('You are dead!', 'adventurebook')) . '

'; // get the page title of the RespawnZone stat $respawn_zone = intval($adventurebook_current_character->get_stat('RespawnZone')) ? intval($adventurebook_current_character->get_stat('RespawnZone')) : get_post_meta($adventurebook_id, '_adventurebook_start_page', true); $respawn_page = get_post($respawn_zone); if ($respawn_page) { /* translators: %s is the respawn page title */ $content .= '

' . sprintf(esc_html(__('Your character has died. You can respawn at %s.', 'adventurebook')), '' . esc_html($respawn_page->post_title) . '') . '

'; } // respawn button $content .= ''; $content .= '

'; // stop further output $content .= '
'; // .adventurebook_inner_content $content .= '
'; // #adventurebook_content $content .= ''; wp_send_json_success($content); } // add the shop output if this is a shop page $content .= adventurebook_get_shop_page_output(); // add the bank output if this is a bank page $content .= adventurebook_get_bank_page_output(); // add enemies $content .= adventurebook_get_enemies_on_page_output(); // if there is a combat output (array of strings), show it under "Combat Log" if (isset($output) && !empty($output)) { $content .= adventurebook_show_combat_log($output); } // add the items on the page output $content .= adventurebook_get_items_on_page_output(); } if ($show_decisions) { // --------------------------- // // Decisions Output // // --------------------------- // add the decisions output $content .= adventurebook_get_decisions_on_page_output($adventurebook_is_game); $content .= ''; // .adventurebook_inner_content $content .= ''; // #adventurebook_content $content .= ''; } // if the enable_inline_editing option is set, add some edit links to the footer // Edit Page, Add Decision, Add Item Spawn, Add Enemy Spawn, Jump to: (page) // All links call a modal window with the respective edit page // Only show this to users with the manage_options capability (admins) // and only if the option is enabled in the settings if (current_user_can('manage_options') && get_option('adventurebook_enable_inline_editing', '0') === '1') { $edit_nonce = wp_create_nonce('adventurebook_inline_edit_nonce'); $content .= '
'; $content .= '

' . esc_html__('Page Editing Tools', 'adventurebook') . '

'; $content .= ''; $content .= ''; $content .= ''; $content .= ''; // Jump-to select box $pages = get_posts(array( 'post_type' => 'adventurebook_page', 'post_parent' => $adventurebook_id, 'post_status' => 'publish', 'numberposts' => -1, 'orderby' => 'title', 'order' => 'ASC', )); if ($pages) { $content .= '
'; $content .= ''; $content .= ''; $content .= '
'; } // Item Spawns $item_spawns = Item_Spawn::get_item_spawns_by_page_id($page_id); if ($item_spawns) { $content .= '

' . esc_html__('Item Spawns on this page', 'adventurebook') . '

'; } // Enemy Spawns $enemy_spawns = Enemy_Spawn::get_enemy_spawns_by_page_id($page_id); if ($enemy_spawns) { $content .= '

' . esc_html__('Enemy Spawns on this page', 'adventurebook') . '

'; } // info about resources to be found on this page ( $content .= adventurebook_get_resource_html_for_this_page($page_id); $content .= '
'; // Container für das Modal $content .= ''; } wp_send_json_success($content); } add_action('wp_ajax_adventurebook_get_page_content', 'adventurebook_get_page_content_ajax'); // add action for non-logged in users add_action('wp_ajax_nopriv_adventurebook_get_page_content', 'adventurebook_get_page_content_ajax'); function adventurebook_show_combat_log($output): string { $content = '
'; $content .= '

' . esc_html(__('Combat Log', 'adventurebook')) . '

'; $content .= '
'; foreach ($output as $line) { // if the line ends with !, make it red if (substr($line, -1) === '!') { $line = '' . esc_html($line) . ''; } else { $line = esc_html($line); } $content .= '
  • ' . $line . '
  • '; } $content .= '
    '; // #adventurebook_combat_log_content $content .= '
    '; // #adventurebook_combat_log return $content; } /** * Returns the HTML output for the character top bar * The Top Bar shows the character name, health, XP, Money and a link to the character management page in one line * When you click on the line, it expands to show the character stats, equipped items and inventory * * @return string */ function adventurebook_get_character_topbar_output(): string { global $page_id, $adventurebook_combat_system, $adventurebook_current_character, $adventurebook_id, $adventurebook_current_character_id, $adventurebook_character_management_url; adventurebook_check_ownership($adventurebook_current_character_id); $content = ''; /** @var Character $adventurebook_current_character */ $content .= '
    '; // add a toggle indicator div $content .= '
    '; // show the current character name and a link to the character management page $content .= '
    '; $content .= ''; $content .= '' . esc_html($adventurebook_current_character->get_name()) . ''; // show CurHealth/Health $stats = $adventurebook_current_character->get_stats(); $current_health = isset($stats['CurHealth']) ? intval($stats['CurHealth']) : 0; $max_health = isset($stats['Health']) ? intval($stats['Health']) : 0; // show health only if health is available if ($max_health > 0) { $content .= ' • ' . esc_html(__('Health', 'adventurebook')) . ': ' . esc_html($current_health) . '/' . esc_html($max_health) . ''; } // show XP and Money (if the combat system is set) if ($adventurebook_combat_system) { $wealth = intval($adventurebook_current_character->get_stat('Wealth')); $wealth_unit = $adventurebook_combat_system->get_wealth_unit(); // usually "G" or "$" or "Gold" $content .= ' • ' . esc_html($wealth_unit) . ': ' . esc_html($wealth) . ''; // check if there is money at the bank (game_var "bank_balance") $bank_balance = intval($adventurebook_current_character->get_game_var('bank_balance')); if ($bank_balance > 0) { $content .= ' (Bank: ' . esc_html($bank_balance) . ')'; } // if the character has a stat "XP", show it if (isset($stats['XP'])) { $xp = intval($stats['XP']); $content .= ' • ' . esc_html(__('XP', 'adventurebook')) . ': ' . esc_html($xp) . ''; } $content .= ''; } $content .= '
    '; // adventurebook_character_title $content .= ''; // adventurebook_character_details $content .= '
    '; // #adventurebook_character_topbar return $content; } /** * Returns the cached HTML output for the character inventory top bar * * @param Character $character The character object * @param Combat_System $combat_System The combat system object * @return string The HTML output */ function adventurebook_get_inventory_topbar_cached(Character $character, Combat_System $combat_System): string { $cache_key = 'adventurebook_inventory_topbar_' . $character->id; $cached_output = get_transient($cache_key); if ($cached_output !== false) { return $cached_output; } else { $output = adventurebook_get_inventory_topbar($character, $combat_System); set_transient($cache_key, $output, 24 * 60 * MINUTE_IN_SECONDS); return $output; } } /** * Returns the cached HTML output for the character inventory * * @param Character $character The character object * @param Combat_System $combat_System The combat system object * @return string The HTML output */ function adventurebook_get_inventory_cached(Character $character, Combat_System $combat_System): string { $cache_key = 'adventurebook_inventory_' . $character->id; $cached_output = get_transient($cache_key); if ($cached_output !== false) { return $cached_output; } else { $output = adventurebook_get_inventory($character, $combat_System); set_transient($cache_key, $output, 24 * 60 * MINUTE_IN_SECONDS); return $output; } } /** * Invalidates the inventory cache for the given character ID * * @param int $id Character ID * @return void */ function adventurebook_invalidate_inventory_cache(int $id): void { $cache_key = 'adventurebook_inventory_topbar_' . $id; delete_transient($cache_key); $cache_key = 'adventurebook_inventory_' . $id; delete_transient($cache_key); } /** * Löscht ALLE gecachten Inventar-/Charakterkarten-HTMLs (alle Charaktere). * * Das Karten-Markup kann sich zwischen Plugin-Versionen ändern (z. B. Div-Struktur- * Fixes). Da adventurebook_get_inventory_cached() das HTML 24 h in Transients hält, * würden sonst nach einem Update alte (u. U. fehlerhafte) Markups weiter ausgeliefert. * Wird daher bei jedem Versionswechsel aufgerufen (siehe adventurebook_check_db_update). * * @return void */ function adventurebook_clear_inventory_caches(): void { global $wpdb; $like_value = $wpdb->esc_like( '_transient_adventurebook_inventory' ) . '%'; $like_timeout = $wpdb->esc_like( '_transient_timeout_adventurebook_inventory' ) . '%'; $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", $like_value, $like_timeout ) ); } /** * Returns the HTML output for the current character and their inventory * * @return string HTML output */ function adventurebook_get_character_output(): string { global $adventurebook_combat_system, $adventurebook_current_character, $adventurebook_character_management_url; $content = ''; /** @var Character $adventurebook_current_character */ $content .= '
    '; // show the current character name and a link to the character management page $content .= '
    '; $content .= '' . esc_html($adventurebook_current_character->get_name()) . ' '; $content .= ''; // show CurHealth/Health $stats = $adventurebook_current_character->get_stats(); $current_health = isset($stats['CurHealth']) ? intval($stats['CurHealth']) : 0; $max_health = isset($stats['Health']) ? intval($stats['Health']) : 0; // show health only if health is available if ($max_health > 0) { // Health bar $pct = (int)(($current_health * 100) / $max_health); if ($pct < 0) { $pct = 0; } if ($pct > 100) { $pct = 100; } $health_pct = $pct; $content .= '
    '; $content .= '' . esc_html(__('Health', 'adventurebook')) . ': ' . esc_html($current_health) . '/' . esc_html($max_health) . ''; } // show XP and Money (if the combat system is set) if ($adventurebook_combat_system) { $wealth = intval($adventurebook_current_character->get_stat('Wealth')); $wealth_unit = $adventurebook_combat_system->get_wealth_unit(); // usually "G" or "$" or "Gold" $content .= ''; $content .= esc_html($wealth_unit) . ': ' . esc_html($wealth) . ''; // check if there is money at the bank (game_var "bank_balance") $bank_balance = intval($adventurebook_current_character->get_game_var('bank_balance')); if ($bank_balance > 0) { $content .= ' (Bank: ' . esc_html($bank_balance) . ')'; } // if the character has a stat "XP", show it if (isset($stats['XP'])) { $xp = intval($stats['XP']); $content .= '  ' . esc_html(__('XP', 'adventurebook')) . ': ' . esc_html($xp) . ''; } $combat_stats = $adventurebook_combat_system->get_combat_stats_output($adventurebook_current_character); $content .= ' ' . implode(' ', $combat_stats) . ''; $content .= ''; } $content .= '
    ' . get_skill_use_bar(); // if the character has any effects, show the effects_output $effects_output = $adventurebook_current_character->get_effects_output(); if (!empty($effects_output)) { $content .= " - " . $effects_output; } $content .= '
    '; // adventurebook_character_title // get the cached inventory $content .= adventurebook_get_inventory_cached($adventurebook_current_character, $adventurebook_combat_system); $content .= '
    '; // #adventurebook_character return $content; } function adventurebook_get_inventory(Character $character, Combat_System $combat_System): string { // EQUIPMENT ITEMS $content = ''; $equipped_items = $character->get_equipped_items(); if (!empty($equipped_items)) { $content .= '
    '; $content .= '
    '; $content .= '' . esc_html(__('Equipped Items', 'adventurebook')) . ''; $content .= '
    '; $content .= ''; // adventurebook_items $content .= '
    '; // adventurebook_items_list } // show the current character inventory // get all items that are owned by the character // get_inventory already returns an array key/value pairs ("item_code" => y, "count" => z) $inventory = $character->get_inventory(); $inventory = adventurebook_sort_items_by_name($inventory); $content .= '
    '; $content .= '
    '; $content .= '' . esc_html(__('Inventory', 'adventurebook')) . ''; $content .= '
    '; // if the inventory is empty, we return here if (empty($inventory)) { $content .= '

    ' . esc_html(__('Your inventory is empty.', 'adventurebook')) . '

    '; $content .= '
    '; // .adventurebook_items_list // WICHTIG: die Karte selbst (#adventurebook_character) schließt der Aufrufer // adventurebook_get_character_output(). Hier NICHT zusätzlich schließen — // ein früheres extra hat sonst #adventurebook_content vorzeitig // geschlossen, wodurch Titel + Seiteninhalt aus dem Grid fielen. return $content; } $content .= render_inventory_items($inventory, $character, $combat_System, $character->id, $character->adventurebook_id); $content .= ''; return $content; } function render_inventory_items($inventory, Character $adventurebook_current_character, Combat_System $adventurebook_combat_system, $adventurebook_current_character_id, $adventurebook_id) : string { $content = ''; return $content; } function get_skill_use_bar(): string { global $adventurebook_is_game, $page_id, $adventurebook_combat_system, $adventurebook_current_character, $adventurebook_id, $adventurebook_current_character_id; $content = ''; // show icons for acquired skills, e.g. a red cross for "firstaid" skill // upon clicking the icon, the skill is used (if usable) $skills = $adventurebook_current_character->get_skills(); if (!empty($skills)) { $content .= ''; foreach ($skills as $skill_code => $skill_info) { // $skill_info is an array with "value" and "cooldown" keys $skill_definition = $adventurebook_combat_system->get_skill_by_code($skill_code); // only show skills that are active (= can be used actively) if ($skill_definition && $skill_definition["active"]) { // if the skill cooldown is > 0 show the icon light grey and unclickable // otherwise show it normally and make it clickable // the skill icon is wrapped in a span with class adventurebook_skill_icon // and an onclick event that calls adventurebook_useSkillAndRefresh(skill_code, character_id, adventurebook_id) $content .= ' 0) { $content .= 'style="opacity:0.4;cursor:not-allowed;"'; } else { $content .= 'onclick="adventurebook_useSkillAndRefresh(\'' . esc_js($skill_code) . '\',' . intval($adventurebook_current_character_id) . ',' . intval($adventurebook_id) . ');" style="cursor:pointer;"'; } $content .= '>'; // the icon is a fa icon $content .= ''; $content .= ''; // adventurebook_skill_icon } } $content .= ''; // adventurebook_skills_list } return $content; } /** * Returns the HTML output for the enemies on the current page * * @return string HTML output */ function adventurebook_get_enemies_on_page_output(): string { global $adventurebook_is_game, $page_id, $adventurebook_combat_system, $adventurebook_current_character, $adventurebook_id, $adventurebook_current_character_id; $content = ''; if ($adventurebook_is_game) { // get all enemies that are on the page $enemies = Enemy::get_enemies_for_page($page_id, $adventurebook_current_character_id); if (!empty($enemies)) { $content .= '
    '; $content .= '

    ' . esc_html(__('Enemies', 'adventurebook')) . '

    '; $content .= ''; $content .= '
    '; } } return $content; } /** * Returns the HTML output for the decisions on the current page * NOTE: if a restriction is set for a decision, the decision will not be shown if the restriction is not met * * @return string HTML output */ function adventurebook_get_decisions_on_page_output($adventurebook_is_game): string { global $page_id, $adventurebook_id, $decisions, $current_character, $adventurebook_current_character_id; $content = ''; // remove decisions that have a restriction that is not met // this is done by checking if the character adventurebook_can_travel_from_zone_to_zone for the decision target // if the decision has no restriction, it is always shown // the function adventurebook_can_travel_from_zone_to_zone checks the restriction // source = current page id, target = decision ['target'] // if we are admin and inline editing is on, we show all decisions, even if the restriction is not met if (!current_user_can('manage_options') || get_option('adventurebook_enable_inline_editing', '0') !== '1') { $decisions = array_filter($decisions, function ($decision) use ($adventurebook_current_character_id, $page_id) { return adventurebook_can_travel_from_zone_to_zone($adventurebook_current_character_id, $page_id, $decision['target']); }); } if (!empty($decisions)) { $content .= '
    '; $content .= '

    ' . esc_html(__('Decisions', 'adventurebook')) . '

    '; $content .= '
    '; } return $content; } /** * Returns the HTML output for the items on the current page * * @return string HTML output */ function adventurebook_get_items_on_page_output(): string { global $adventurebook_is_game, $page_id, $adventurebook_combat_system, $adventurebook_id, $adventurebook_current_character_id; $content = ''; if ($adventurebook_is_game) { // get all items that are on the page $items = get_items_for_page($page_id, $adventurebook_current_character_id); // sort by name $items = adventurebook_sort_items_by_name($items); if (!empty($items)) { $content .= '
    '; $content .= '

    ' . __('Items', 'adventurebook') . '

    '; $content .= ''; $content .= '
    '; } } return $content; } /** * Returns the HTML output for the bank on the current page * In a bank, the character can deposit and withdraw money (stat: "Wealth") * The bank account balance is saved in the character game_var "bank_balance" * Shown is the actual balance and two buttons: "Deposit" and "Withdraw" * Both will show a JavaScript prompt to enter the amount to deposit/withdraw * * @return string HTML output */ function adventurebook_get_bank_page_output(): string { global $adventurebook_is_game, $page_id; $content = ''; $page_type = get_post_meta($page_id, '_adventurebook_page_type', true); if ($adventurebook_is_game && $page_type === 'bank') { $content .= '
    '; $content .= '

    ' . esc_html(__('Bank', 'adventurebook')) . '

    '; // show the character's bank balance global $adventurebook_current_character, $adventurebook_combat_system; /** @var Character $adventurebook_current_character */ $bank_balance = intval($adventurebook_current_character->get_game_var('bank_balance', 0)); $wealth_unit = $adventurebook_combat_system->get_wealth_unit(); $content .= '
    '; $content .= '' . // translators: %d is the bank balance esc_html(sprintf(__('Your Bank Balance: %d', 'adventurebook'), $bank_balance)) . ' ' . esc_html($wealth_unit) . ''; $content .= '
    '; // show Deposit and Withdraw buttons // Deposit: sent the character's current Wealth as parameter and prompt for amount to deposit // Withdraw: prompt for amount to withdraw, bank balance as parameter $content .= '
    '; $content .= ''; $content .= '  '; $content .= ''; $content .= '
    '; $content .= '
    '; // adventurebook-bank-content } return $content; } function adventurebook_deposit_to_bank_ajax(): void { do_log("adventurebook_deposit_to_bank_ajax called"); check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren $adventurebook_id = intval(ab_get('adventurebook_id')); $adventurebook_combat_system = adventurebook_get_combat_system_for_adventurebook($adventurebook_id); if (!$adventurebook_combat_system) { wp_send_json_error(esc_html(__('Combat system not found.', 'adventurebook'))); } $character_id = intval(ab_get('character_id')); $amount = intval(ab_get('amount')); adventurebook_check_ownership($character_id); $character = Character::get_character_by_id($character_id); $current_wealth = intval($character->get_stat('Wealth')); if ($amount < 0) { $amount = 0; } if ($amount > $current_wealth) { $amount = $current_wealth; } do_log("Depositing amount: $amount"); // decrease character wealth $character->set_stat('Wealth', $current_wealth - $amount); // increase bank balance $bank_balance = intval($character->get_game_var('bank_balance', 0)); $character->set_game_var('bank_balance', $bank_balance + $amount); // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_deposit_to_bank', 'adventurebook_deposit_to_bank_ajax'); function adventurebook_withdraw_from_bank_ajax(): void { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren $adventurebook_id = intval(ab_get('adventurebook_id')); $adventurebook_combat_system = adventurebook_get_combat_system_for_adventurebook($adventurebook_id); if (!$adventurebook_combat_system) { wp_send_json_error(esc_html(__('Combat system not found.', 'adventurebook'))); } $character_id = intval(ab_get('character_id')); $amount = intval(ab_get('amount')); adventurebook_check_ownership($character_id); $character = Character::get_character_by_id($character_id); $bank_balance = intval($character->get_game_var('bank_balance', 0)); if ($amount < 0) { $amount = 0; } if ($amount > $bank_balance) { $amount = $bank_balance; } // decrease bank balance $character->set_game_var('bank_balance', $bank_balance - $amount); // increase character wealth $current_wealth = intval($character->get_stat('Wealth')); $character->set_stat('Wealth', $current_wealth + $amount); // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } add_action('wp_ajax_adventurebook_withdraw_from_bank', 'adventurebook_withdraw_from_bank_ajax'); /** * Returns the HTML output for the resource gathering on the current page * * e.g. Wood: 2400, Iron: 200 * The Label is taken from the combat system resource definitions * * option: $stored = get_post_meta($post->ID, '_adventurebook_page_resources', true); * * @param int $page_id The current page ID * @return string HTML output */ function adventurebook_get_resource_html_for_this_page(int $page_id) : string { $resources = get_post_meta($page_id, '_adventurebook_page_resources', true); // it's a string in the form "wood:2400;iron:200" // explode the resources into an array $resources_array = explode(';', $resources); $resources = array(); foreach ($resources_array as $resource_string) { $parts = explode(':', $resource_string); if (count($parts) === 2) { $resource_code = trim($parts[0]); $amount = intval(trim($parts[1])); if ($amount > 0) { $resources[$resource_code] = $amount; } } } if (empty($resources)) { return ''; } global $adventurebook_id; $adventurebook_combat_system = adventurebook_get_combat_system_for_adventurebook($adventurebook_id); if (!$adventurebook_combat_system) { return ''; } $available_resources = $adventurebook_combat_system->get_available_resources(); // simply output the resources (Wood: 2400, Iron: 200) $content = '
    '; foreach ($resources as $resource_code => $amount) { $resource_label = $available_resources[$resource_code] ?? ucfirst($resource_code); $content .= '' . esc_html($resource_label) . ': ' . esc_html(strval($amount)). ''; } $content .= '
    '; return $content; } /** * Returns the HTML output for the harvesting on the current page * (What is harvested, how many minutes have passed, button for end harvesting) * * if the character is harvesting, the game_var "harvesting" is set to "1" * e.g. * $combatant->set_game_var('harvesting', '1'); * $combatant->set_game_var('harvesting_start_time', strval(time())); * $combatant->set_game_var('harvesting_resource', 'wood'); * * @return string HTML output */ function adventurebook_get_harvesting_output(): string { global $adventurebook_is_game, $adventurebook_current_character, $adventurebook_id, $adventurebook_current_character_id; $content = ''; if ($adventurebook_is_game) { $adventurebook_combat_system = adventurebook_get_combat_system_for_adventurebook($adventurebook_id); if (!$adventurebook_combat_system) { return ''; } // if the combat system does not have any resources defined, return empty if ($adventurebook_combat_system->get_available_resources() === []) { return ''; } /** @var Character $adventurebook_current_character */ $harvesting = $adventurebook_current_character->get_game_var('harvesting'); $harvesting_start = intval($adventurebook_current_character->get_game_var('harvesting_start_time')); $harvesting_resource = $adventurebook_current_character->get_game_var('harvesting_resource'); if ($harvesting === '1' && $harvesting_start > 0 && $harvesting_resource) { // get the 24h resource yield for this resource from the combat system $yield_so_far = 0; $resource_yield_24h = $adventurebook_combat_system->get_resource_yield_for_page($adventurebook_current_character->location_id, $harvesting_resource); // the resource yield is at least 1, else harvesting couldn't have been started // calculate how much percent of 1 hour have passed since harvesting started $now = time(); $seconds_passed = $now - $harvesting_start; $percent_passed = min(1.0, $seconds_passed / (60 * 60)); $yield = intval(floor($resource_yield_24h * $percent_passed)); $harvesting_skill_code = $adventurebook_combat_system->get_skill_code_for_harvesting_resource($harvesting_resource); $harvesting_skill_level = intval($adventurebook_current_character->get_skill_value($harvesting_skill_code)); // the final yield capped by skill_level * 10 (with skill 4, max yield is 40) $max_yield_by_skill = $harvesting_skill_level * 10; if ($max_yield_by_skill > 0 && $yield > $max_yield_by_skill) { $yield = $max_yield_by_skill; } // calculate how many minutes have passed since harvesting started $now = time(); $minutes_passed = intval(($now - $harvesting_start) / 60); $content .= '
    '; $content .= '

    ' . esc_html(__('Harvesting', 'adventurebook')) . '

    '; $content .= '

    ' . // translators: %s is the resource being harvested esc_html(sprintf(__('You are harvesting %s.', 'adventurebook'), ucfirst($harvesting_resource))) . '

    '; $content .= '

    ' . // translators: %d is the number of minutes passed esc_html(sprintf(__('Minutes passed: %d', 'adventurebook'), $minutes_passed)) . '

    '; $content .= '

    ' . // translators: %d is the amount of resource harvested so far esc_html(sprintf(__('You have harvested %d units so far.', 'adventurebook'), $yield)) . '

    '; $content .= ''; $content .= '
    '; } } return $content; } /** * AJAX function to stop harvesting */ add_action('wp_ajax_adventurebook_stop_harvesting', 'adventurebook_stop_harvesting_ajax'); function adventurebook_stop_harvesting_ajax(): void { check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren $adventurebook_id = intval(ab_get('adventurebook_id')); $character_id = intval(ab_get('character_id')); adventurebook_check_ownership($character_id); $character = Character::get_character_by_id($character_id); // if the character is not harvesting, do nothing if (!$character->is_harvesting()) { adventurebook_get_page_content_ajax(); return; } $character->stop_harvesting(); // refresh the page content adventurebook_get_page_content_ajax(); } /** * Returns the HTML output for the shop on the current page * * @return string HTML output */ function adventurebook_get_shop_page_output(): string { global $adventurebook_is_game, $page_id, $adventurebook_combat_system, $adventurebook_current_character, $adventurebook_id, $adventurebook_current_character_id; /* @var Combat_System $adventurebook_combat_system */ $content = ''; /** @var Character $adventurebook_current_character */ // if this is a game, and the page type is "shop", show additional shop information // - a list of items that are for sale with their prices (stats['price']) * the shop price factor $page_type = get_post_meta($page_id, '_adventurebook_page_type', true); if ($adventurebook_is_game && $page_type === 'shop') { $shop_price_factor = get_post_meta($page_id, '_adventurebook_shop_price_factor', true); $shop_price_factor = floatval($shop_price_factor); if ($shop_price_factor <= 0) { $shop_price_factor = 1.0; } // get all items that are for sale on this page (_adventurebook_shop_items meta field contains a comma-separated list of item codes) $shop_items_codes = get_post_meta($page_id, '_adventurebook_shop_items', true); $shop_items_codes = is_array($shop_items_codes) ? $shop_items_codes : explode(',', $shop_items_codes); $shop_items_codes = array_map('trim', $shop_items_codes); $shop_items_codes = array_filter($shop_items_codes); if (!empty($shop_items_codes)) { // get the item definitions from the database global $wpdb; $shop_items = []; foreach ($shop_items_codes as $code) { $shop_items[] = Item_Definition::get_item_definition_by_code($code); } // sort by $shop_item->base_price ascending usort($shop_items, function ($a, $b) { return $a->get_base_price() <=> $b->get_base_price(); }); if (!empty($shop_items)) { $content .= '
    '; $content .= '

    ' . esc_html(__('Shop', 'adventurebook')) . '

    '; // show the character's wealth $character_stats = $adventurebook_current_character->get_stats(); $character_wealth = isset($character_stats['Wealth']) ? intval($character_stats['Wealth']) : 0; $content .= '
    '; $content .= '' . // translators: %d is the character's wealth esc_html(sprintf(__('Your Wealth: %d', 'adventurebook'), $character_wealth)) . ' ' . $adventurebook_combat_system->get_wealth_unit() . ''; // if the character has the game_var "bank_balance", show it as well $bank_balance = intval($adventurebook_current_character->get_game_var('bank_balance', 0)); if ($bank_balance > 0) { $content .= ' (' . // translators: %d is the bank balance esc_html(sprintf(__('Bank: %d', 'adventurebook'), $bank_balance)) . ' ' . $adventurebook_combat_system->get_wealth_unit() . ')'; } $content .= '
    '; // get the price wealth_unit from the combat system $wealth_unit = $adventurebook_combat_system->get_wealth_unit(); $content .= ''; foreach ($shop_items as $item_definition) { /* @var Item_Definition $item_definition */ $stats = $item_definition->get_stats(); // get the statname for the item price from the combat system $statname = $adventurebook_combat_system ? $adventurebook_combat_system->get_item_stat_name_for_price() : 'Price'; // get the price from the stats array $price = $item_definition->get_base_price(); $price = intval($price * $shop_price_factor); // only show items that have a price > 0 // show td: image, item name, combat stats, price, buy button if ($price > 0) { $content .= ''; $content .= ''; $content .= ''; $content .= ''; $content .= ''; $content .= ''; $content .= ''; } } $content .= '
    '; $content .= ''; $content .= ''; $content .= '' . esc_html($item_definition->name) . ''; //$content .= '
    '.esc_html( $item_definition->description ).''; $content .= '
    '; $content .= esc_html($price) . ' ' . esc_html($wealth_unit); $content .= ''; $content .= ''; $content .= '
    '; // show combat stats $combat_stats = $adventurebook_combat_system->get_item_combat_stats_output($item_definition); $content .= implode(' ', $combat_stats); $content .= '
    ' . $item_definition->get_description() . '
    '; $content .= '
    '; } } } return $content; } function adventurebook_try_decision_ajax() { // check for nonce check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren global $adventurebook_id; // needed here for adventurebook_can_travel_from_zone_to_zone function if (isset($_POST['source_page_id'])) { $source_page_id = intval($_POST['source_page_id']); } if (isset($_POST['destination_page_id'])) { $destination_page_id = intval($_POST['destination_page_id']); } if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } // check if this is a game $adventurebook_is_game = adventurebook_is_game($adventurebook_id); if ($adventurebook_is_game) { // check if the user is logged in if (!is_user_logged_in()) { status_header(400); wp_send_json_error(__('You need to log in to play the game.', 'adventurebook')); } $user_id = get_current_user_id(); // Get the current character_id of the user as integer $current_character_id = (int)get_user_meta($user_id, 'adventurebook_current_character_id_' . $adventurebook_id, true); adventurebook_check_ownership($current_character_id); $character = Character::get_character_by_id($current_character_id); $character->stop_working(); if (adventurebook_can_travel_from_zone_to_zone($current_character_id, $source_page_id, $destination_page_id)) { // check if the zone has hostile mobs // in this case, the chance is 50% that the character can not leave the zone $hostile_mobs = Enemy::location_has_hostile_enemies($source_page_id, $current_character_id); if (!empty($hostile_mobs)) { $random_number = wp_rand(1, 100); if ($random_number <= 50) { add_transient_message(esc_html(__('Your enemies won\'t let you go!', 'adventurebook'))); adventurebook_get_page_content_ajax(); exit; } } // set the destination_page as the actual zone for the character $character->move_to_zone($destination_page_id); // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } else { add_transient_message(__('You can not go this way.', 'adventurebook')); adventurebook_get_page_content_ajax(); } } else { if (adventurebook_can_travel_from_zone_to_zone(0, $source_page_id, $destination_page_id)) { // set the adventurebook_id AND the page_id in the session adventurebook_save_to_session('adventurebook_page_id_' . $adventurebook_id, $destination_page_id); // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } else { status_header(403); wp_send_json_error(esc_html(__('You can not go this way.', 'adventurebook'))); } } } add_action('wp_ajax_adventurebook_try_decision', 'adventurebook_try_decision_ajax'); // add action for non-logged in users add_action('wp_ajax_nopriv_adventurebook_try_decision', 'adventurebook_try_decision_ajax'); /** * teleports the current character to the destination zone * but only if his (the current) user has admin rights * * @return void */ function adventurebook_teleport_ajax() { // check for nonce check_ajax_referer('adventurebook_ajax_nonce'); // Nonce validieren global $adventurebook_id; // needed here for adventurebook_can_travel_from_zone_to_zone function if (isset($_POST['destination_page_id'])) { $destination_page_id = intval($_POST['destination_page_id']); } if (isset($_POST['adventurebook_id'])) { $adventurebook_id = intval($_POST['adventurebook_id']); } // check if the user is admin if (!current_user_can('manage_options')) { // Benutzer hat Adminrechte wp_send_json_error(__('No sufficient rights.', 'adventurebook')); } // check if this is a game $adventurebook_is_game = adventurebook_is_game($adventurebook_id); if ($adventurebook_is_game) { // check if the user is logged in if (!is_user_logged_in()) { status_header(400); wp_send_json_error(__('You need to log in to play the game.', 'adventurebook')); } global $wpdb; $user_id = get_current_user_id(); // Get the current character_id of the user as integer $current_character_id = (int)get_user_meta($user_id, 'adventurebook_current_character_id_' . $adventurebook_id, true); adventurebook_check_ownership($current_character_id); // set the destination_page as the actual zone for the character $character = Character::get_character_by_id($current_character_id); $character->stop_working(); $character->move_to_zone($destination_page_id); // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } else { // set the adventurebook_id AND the page_id in the session adventurebook_save_to_session('adventurebook_page_id_' . $adventurebook_id, $destination_page_id); $response = array( 'status' => 'success', 'destination_page_id' => $destination_page_id, ); // now we need to call the adventurebook_get_page_content_ajax function to get the updated page content adventurebook_get_page_content_ajax(); } } add_action('wp_ajax_adventurebook_teleport', 'adventurebook_teleport_ajax'); /** * Liefert die erste Seite oder Post, die den AdventureBook-Shortcode mit einer bestimmten ID enthält. * * @param int $id Die ID des AdventureBook-Shortcodes * * @return WP_Post|null Die gefundene Seite/Post oder null, wenn keine gefunden wurde */ function adventurebook_get_first_page_using_shortcode(int $id): ?WP_Post { global $wpdb; // Bereite die Suche vor: wir suchen nach [adventurebook_start_page="ID"] $like = '%[adventurebook%id="' . $wpdb->esc_like($id) . '"%]%'; // SQL-Abfrage, die nur die erste passende Seite/Post zurückliefert $post_id = $wpdb->get_var(// phpcs:ignore WordPress.DB.DirectDatabaseQuery $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type IN ('post','page') AND post_content LIKE %s LIMIT 1", $like ) ); if ($post_id) { return get_post($post_id); } return null; } /** * Fügt eine Transient-Nachricht hinzu, die auf der nächsten Seite angezeigt wird. * Es wird geschaut ob es bereits eine Nachricht (ein Array) in adventurebook_messages gibt, wenn ja wird die neue Nachricht angehängt. * * @param string $message Die Nachricht, die angezeigt werden soll */ function add_transient_message(string $message) { $messages = get_transient('adventurebook_messages'); if (!is_array($messages)) { $messages = array(); } $messages[] = $message; set_transient('adventurebook_messages', $messages, 30); // 30 Sekunden gültig } function adventurebook_save_to_session($key, $value) { // Session starten, falls sie noch nicht gestartet wurde if (!session_id()) { session_start(); } // Wert in der Session speichern $_SESSION[$key] = $value; } function adventurebook_sort_items_by_name($items) { // sort the items by their name usort($items, function (Item $a, Item $b) { $name_a = $a->get_name() ?? ''; $name_b = $b->get_name() ?? ''; return strcmp($name_a, $name_b); }); return $items; } function do_log($message) { if (defined('WP_DEBUG') && WP_DEBUG) { if (is_array($message) || is_object($message)) { error_log(print_r($message, true)); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r, WordPress.PHP.DevelopmentFunctions.error_log_var_dump, WordPress.PHP.DevelopmentFunctions.error_log_error_log } else { error_log($message); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r, WordPress.PHP.DevelopmentFunctions.error_log_var_dump, WordPress.PHP.DevelopmentFunctions.error_log_error_log } } }