connect_error) { echo "00 DATABASE CONNECTION ERROR\n"; error_log(sprintf("{$_SERVER['REMOTE_ADDR']} Database connection error: %s", $mysqli->connect_error)); return; } $mysqli->set_charset("utf8mb4"); $mysqli->autocommit(false); register_shutdown_function(function() { global $mysqli; if ($mysqli) { $mysqli->close(); } }); function sandbox_pvp_log($message) { global $sandbox; if ($sandbox) error_log($message); } function sandbox_log($message) { global $sandbox; if ($sandbox) error_log($message); } foreach ($_REQUEST as $key => $value) { if (is_string($value)) $_REQUEST[$key] = $mysqli->real_escape_string($value); } // define('MEMCACHED_HOST', '127.0.0.1'); // define('MEMCACHED_PORT', '11211'); // $memcache = new Memcached; // $cache_available = $memcache->addServer(MEMCACHED_HOST, MEMCACHED_PORT); $json_output = false; function escape_sql_string($string) { global $mysqli; return $mysqli->real_escape_string($string); } function sql($sql) { global $mysqli; global $command_log; global $json_output, $sql_error; global $_REQUEST; if (@$_REQUEST["debug"]) { global $time_start; $time = microtime(true) - $time_start; echo "\n<-- $time: $sql -->\n"; } $retries = 0; while (true) { try { $result = $mysqli->query($sql); break; } catch (Exception $e) { if ($e->getCode() == 1213 && $retries < 15) { error_log("PHP Warning: Deadlock detected in SQL, $retries retries so far: $sql"); ++$retries; usleep(10000); continue; } else { error_log("SQL ERROR\n$sql\n{$mysqli->error}"); $result = null; break; } } } if ($result) { if (preg_match("/^(insert|delete|update|call|replace|create|alter)/i", $sql)) { $count = (int)$mysqli->affected_rows; // error_log("SQL $sql: $count rows affected"); $mysqli->commit(); return $count; } else { $result = $result->fetch_object(); $count = count((array)$result); if ($count == 0) return; if ($count == 1) { $values = array_values((array)$result); return $values[0]; } return $result; } } if (!function_exists("get_caller_info")) require_once "version6/debuginfo.php"; $caller = get_caller_info(); error_log("SQL ERROR $caller\n$sql\n{$mysqli->error}"); if (!$json_output) { //echo "00 SQL ERROR\n"; echo "00 SQL ERROR\n$sql\n" . $mysqli->error; error_log("Rollback SQL"); $mysqli->rollback(); exit(); } $sql_error = $mysqli->error; return false; } function sql_multiple($sql) { global $mysqli, $_REQUEST, $json_output, $sql_error; if (@$_REQUEST["debug"]) { global $time_start; $time = microtime(true) - $time_start; echo "<-- $time: $sql -->\n"; } $result = $mysqli->query($sql); if ($result) return $result; if (!$json_output) { printf("00 SQL ERROR\n%s\n%s", $sql, $mysqli->error); error_log("SQL ERROR\n$sql\n{$mysqli->error}"); error_log("Rollback"); $mysqli->rollback(); $mysqli = null; exit(); } $sql_error = $mysqli->error; return false; } function sql_join($sql) { global $mysqli, $_REQUEST, $json_output, $sql_error; if (array_key_exists("debug", $_REQUEST)) { global $time_start; $time = microtime(true) - $time_start; echo "<-- $time: $sql -->\n"; } $result = $mysqli->query($sql); if ($result) { $list = array(); $as_number = true; while (($row = $result->fetch_array()) != null) { if (!is_numeric($row[0])) $as_number = false; $list[] = $row[0]; } if (!$list) return ""; return $as_number ? join(",",$list) : "'".join("','",$list)."'"; } if (!$json_output) { printf("00 SQL ERROR\n%s\n%s", $sql, $mysqli->error); error_log("SQL ERROR\n$sql\n{$mysqli->error}"); exit(); } $sql_error = $mysqli->error; return false; } function translate($text) { global $lang; $search = escape_sql_string($text); $translation = sql("select translated from z_translations where text = '$search' and language = '$lang'"); if ($translation) { $translation = str_replace(['\\n', '\\r', '\\t'], ["\n", "\r", "\t"], $translation); return $translation; } else { return $text; } } 4 && !$strange_chars; }name; $name = str_replace(":", " ", $profile->name); $last_attack = str_replace(":", ".",$profile->last_attack); // if (@$profile->keys > 999) // $profile->keys = 999; printf("%s:%s:level=%d:energy=%d/%d:health=%d/%d:money=%s:jewels=%s:medals=%d:keys=%d:swords=%d:xp=%s/%s-%s:attack=%d:defense=%d:zombies=%s:country=%s:head=%d:male=%d:health_cost=%d:energy_cost=%d:wins=%d:loses=%d:max_friends=%d:mercs=%d:last_attack=%s:shield=%d:shield_seconds=%d:rage_seconds=%d:energy_seconds=%d:health_seconds=%d:gems=%d:guild=%s:guild_job=%d:guild_medals=%d:elo=%d:developer=%d%s\n", $profile->token, $name, $profile->level, $profile->energy, $profile->energy_max, $profile->health, $profile->health_max, $profile->money, $profile->jewels, (int)@$profile->medals, (int)@$profile->keys, (int)@$profile->swords, $profile->xp, $profile->xp_prev, $profile->xp_next, $profile->attack, $profile->defense, $profile->zombies, $profile->country, $profile->head, $profile->male, $profile->health_cost, $profile->energy_cost, $profile->pvp_wins, $profile->pvp_loses, $profile->max_friends, $profile->mercs, $last_attack, $profile->shield, $profile->shield_seconds < 0 ? 0 : $profile->shield_seconds, $profile->rage_seconds < 0 ? 0 : $profile->rage_seconds, @$profile->energy_seconds, @$profile->health_seconds, $profile->gems, @$profile->guild, @$profile->guild_job, @$profile->guild_medals, @$profile->elo, @$profile->developer, $extras); } function dump_pvp_profile($profile, $extras = "") { global $redis, $token; if (strlen($profile->token) == 0) return; $start = microtime(true); $server = (int)$profile->server; if ($server) { if (check_redis()) { $rank = $redis->zRevRank("scores-$server", $profile->token); if ($rank !== false) { $rank += 1; $extras .= ":rank=$rank"; } } } // If vip_end is in the past, remove VIP $vip_1_remaining = 0; $vip_2_remaining = 0; if (!$profile->vip) { $profile->vip = 0; } if ($profile->vip_1_end) { $vip_1_remaining = strtotime($profile->vip_1_end) - time(); if ($vip_1_remaining < 0) { $vip_1_remaining = 0; } } if ($profile->vip_2_end) { $vip_2_remaining = strtotime($profile->vip_2_end) - time(); if ($vip_2_remaining < 0) { $vip_2_remaining = 0; } } if ($profile->vip < 2 && $vip_2_remaining > 0) { $profile->vip = 2; sql("update z_players set vip = 2 where token = '$profile->token'"); } elseif ($profile->vip < 1 && $vip_1_remaining > 0) { $profile->vip = 1; sql("update z_players set vip = 1 where token = '$profile->token'"); } elseif ($profile->vip == 2 && $vip_2_remaining <= 0) { $profile->vip = $vip_1_remaining > 0 ? 1 : 0; sql("update z_players set vip = $profile->vip where token = '$profile->token'"); } elseif ($profile->vip == 1 && $vip_1_remaining <= 0) { $profile->vip = 0; sql("update z_players set vip = $profile->vip where token = '$profile->token'"); } $extras .= ":vip={$profile->vip}:vip_1_remaining={$vip_1_remaining}:vip_2_remaining={$vip_2_remaining}"; if ($token == $profile->token) { if ($profile->zombie_multiplier_seconds <= 0) { $profile->zombie_multiplier = 1; $profile->zombie_multiplier_seconds = 0; } $jewels_purchased_today = (int)$profile->jewels_purchased_today; $extras .= ":chips={$profile->chips}" .":fuel={$profile->fuel}" .":deadly_coins={$profile->deadly_coins}" .":black_jewels={$profile->black_jewels}" .":jingle_bells={$profile->jingle_bells}" .":marshmallows={$profile->marshmallows}" .":cupid_cookies={$profile->cupid_cookies}" .":shamrocks={$profile->shamrocks}" .":doubloons={$profile->doubloons}" .":tomoe_coins={$profile->tomoe_coins}" .":chaos_cookies={$profile->chaos_cookies}" .":naira_coins={$profile->naira_coins}" .":coral_coins={$profile->coral_coins}" .":rage_multiplier={$profile->rage_multiplier}" .":zombie_multiplier={$profile->zombie_multiplier}" .":zombie_multiplier_seconds={$profile->zombie_multiplier_seconds}" .":jewels_purchased_today={$jewels_purchased_today}" .":server={$profile->server}"; if ($profile->piggy_expires) { $piggy_expires = strtotime($profile->piggy_expires) - time(); if ($piggy_expires < 0) { $piggy_expires = 0; } $extras .= ":piggy_expires={$piggy_expires}:piggy_energy={$profile->piggy_energy}"; } else { $extras .= ":piggy_expires=0"; } } /* -- No longer used $active_areas = get_active_areas(); $vs = sql("select *, time_to_sec(timediff(killstreak_expires, now())) as killstreak_seconds from z_event_stats_vs where player_token = '{$profile->token}' and area_code in ($active_areas)"); if ($vs) { $stats = sql("select * from z_event_ranks_vs where rank = {$vs->rank}"); $nextCP = (int)sql("select cp_required from z_event_ranks_vs where cp_required > {$vs->cp} order by cp_required"); $extras .= sprintf(":vs=%d,%d,%d,%d,%d,%d,%d,%d,%d", $vs->rank, $vs->cp, $vs->dogtags, $vs->killstreak, $vs->killstreak_seconds, $stats->cp_required, $nextCP, $stats->cp_reward, $vs->prestige); } */ dump_profile($profile, $extras); $end = microtime(true) - $start; if ($end > 0.05) error_log("Dumping profile for {$profile->name} took $end seconds"); } function dump_boss_battle($battle_code, $prefix, $ignore_oldies = false) { global $token; $battle = get_event_battle($battle_code); //error_log("Battle $battle_code: {$battle->battle_code} area {$battle->area_code} boss {$battle->class}"); if (!$battle) return; if ($ignore_oldies && $battle->seconds < -8*3600 && $battle->won_by == "" && $battle->health > 0) return; if ($battle) { $attacked = sql("select 1 from z_event_battle_log where battle_code = $battle_code and player_token = '$token'"); $boss = get_boss ($battle->area_code, $battle->instance_code, $battle->class, $battle->level); $starter = get_player_name($battle->started_by); $you_win = ($token === $battle->won_by); $help_allowed = $boss ? $boss->help_allowed : 0; if (!$attacked) $help_allowed = 0; $friends_enabled = (int)get_game_variable("friends_enabled"); if (!$friends_enabled) $help_allowed = 0; $rank = 1; if ($battle->foe) { $info = sql("select name, head, male, attack, defense, health from z_players where token = '{$battle->foe}'"); if (!$boss) { $boss = new stdClass(); $boss->defense = $info->defense; $boss->attack = $info->attack; $boss->gives_keys = 0; $boss->gives_swords = 0; $boss->help_allowed = 0; $boss->zombies = 0; } $rank_info = sql("select * from z_event_ranks_vs where rank = ifnull( (select rank from z_event_stats_vs where area_code = {$battle->area_code} and player_token = '{$battle->foe}'), 1)"); if ($rank_info && $boss) $boss->zombies = $rank_info->cp_reward; $rank = @$rank_info->rank; } $instance_info = ""; if ($battle->instance_code) { $instance = sql("select * from z_event_raid_instances where code = {$battle->instance_code}"); if ($instance) { $instance_info = ":instance_completed={$instance->completed}". ":instance_dismissed={$instance->dismissed}"; } } if ($battle->skills) { $battle->skills = preg_replace("/:/", "|", $battle->skills); } echo( "$prefix". ":started_by={$battle->started_by}". ":win=$you_win:won_by={$battle->won_by}". ":player_name=$starter". ":code={$battle->battle_code}". ":area_code={$battle->area_code}". ":seconds={$battle->seconds}". ":total={$battle->total}". ":started={$battle->started}". ":class={$battle->class}". ":level={$battle->level}". ":name={$boss->name}". ":sprite={$boss->sprite}". ":health={$battle->health}". ":max_health={$battle->max_health}". ":attack={$boss->attack}". ":defense={$boss->defense}". ":zombies={$boss->zombies}". ":gives_keys={$boss->gives_keys}". ":gives_swords={$boss->gives_swords}". ":attacked=$attacked". ":help_allowed={$help_allowed}". ":gem={$battle->gem}". ":foe={$battle->foe}". ":rank=$rank". ":raid_code={$battle->raid_code}". ":instance_code={$battle->instance_code}$instance_info". ":regeneration={$battle->regeneration}". ":poison={$battle->poison}". ":skills={$battle->skills}". ":cost_multiplier={$battle->energy_cost}". ":health_defense={$battle->health_defense}\n" ); } return $battle; } function dump_full_player_profile($token, $extras = "") { global $travel_km_per_second; global $client; $player = get_player_profile($token); // Generate invite code if not present if (!$player->invite_code) { for(;;) { $invite_code = rand(100000000, 999999999); $exists = sql("select 1 from z_players where invite_code = $invite_code"); if (!$exists) break; } sql("update z_players set invite_code = $invite_code where token = '$token'"); $player->invite_code = $invite_code; } $extras = ":invite_code={$player->invite_code}".$extras; dump_pvp_player_profile($token, $extras); dump_player_hideout($token); dump_player_inventory($token); dump_player_achievements($token); dump_player_schematics($token); dump_player_gunsmith($token); dump_player_mercs($token); $instance = sql("select * from z_event_raid_instances where started_by = '$token' and raid_code = 0 and completed = 0 and dismissed = 0"); if ($instance) { require_once "version6/raid.php"; $battle = get_instance_battle($instance); dump_raid(0, $instance, $battle); } global $merc_slots; if ($player->mercs < $merc_slots) { /* Add mercs from hideout */ sql("update z_players set mercs = $merc_slots where token = '$token'"); error_log("Player $token $player->name had less mercs than hideout, updated to $merc_slots"); clear_player_cache($token); } $help_request = get_help_requests($token); $pending_help = array_filter(get_help_requests_to($token), fn($r) => !$r->accepted); foreach ($pending_help as $row) printf("pending_help:%s:%d:%d:%d\n", $row->player_token, (int)$row->elapsed, (int)$row->accepted, (int)$row->for_hideout); foreach ($help_request as $row) printf("help_request:%s:%d:%d:%d:%d\n", $row->friend_token, (int)$row->elapsed, (int)$row->accepted, (int)$row->for_hideout, (int)$row->confirmed); $in_tutorial = $player->level < get_game_variable("event_unlock_level"); $active_areas = get_active_areas($in_tutorial); echo "active_areas:$active_areas\n"; $active_event = get_active_event($in_tutorial); if ($active_event) { $energy_spent = (int)sql("select energy_spent from z_event_energy_spent where player_token = '$token' and area_code = $active_event"); echo "energy_spent:$energy_spent\n"; } $event_features = get_event_features($in_tutorial); if ($event_features) { echo "event_features:" . implode(":", array_map(function($f) { $o = (array)$f; return implode(",", array_map(function($k, $v) { return "$k=$v"; }, array_keys($o), $o)); }, $event_features)) . "\n"; } $event_stats = sql_multiple("select *, time_to_sec( timediff(now(), km_update) ) as seconds, reached is not null as reached, time_to_sec( timediff(now(), reached) ) as seconds_reached from z_event_stats where player_token = '$token' and area_code in ($active_areas)"); while (($row = $event_stats->fetch_object()) != null) { /* --- VS no longer applies $vs = sql("select *, time_to_sec(timediff(killstreak_expires, now())) as killstreak_seconds from z_event_stats_vs where player_token = '$token' and area_code = {$row->area_code}"); if ($vs) { $rank = (int)sql("select max(rank) from z_event_ranks_vs where cp_required <= {$vs->cp}"); $prevCP = (int)sql("select cp_required from z_event_ranks_vs where cp_required <= {$vs->cp} order by rank desc"); $nextCP = (int)sql("select cp_required from z_event_ranks_vs where cp_required > {$vs->cp}"); if (!$vs->rank) $vs->rank = 1; printf("vs:%d:%d:%d:%d:%d:%d:%d:%d\n", $vs->rank, $vs->cp, $vs->dogtags, $vs->killstreak, $vs->killstreak_seconds, $prevCP, $nextCP, $vs->prestige); } */ $added = 0; $expires = 0; $travel = (int)$row->km - (int)$row->km_boss + (int)$row->km_pending; $total = (int)$row->km + (int)$row->km_pending; if ($total > 0) { $finish_line = sql("select max(km) from z_route_points where area_code = {$row->area_code}"); if (!$finish_line) { error_log("PHP Error: $token {$player->name} No finish line for area {$row->area_code}"); $finish_line = 10000000; } if ($row->km_pending > 0) { $expires = -(int)$row->seconds + (int)$row->km_pending / $travel_km_per_second; if ($expires <= 0) { $added = $row->km_pending; $row->km += $added; if ($row->km >= $finish_line) { $row->km = $finish_line; $row->reached = 1; $row->seconds_reached = -$expires; sql("update z_event_stats set reached = date_add(now(), interval $expires second), km = $finish_line, km_pending = 0, km_update = now() where player_token = '$token' and area_code = {$row->area_code} and reached is null"); error_log("$token reached the finish line at event {$row->area_code}"); $expires = 0; } else { sql("update z_event_stats set km_update = date_add(now(), interval $expires second), km = km + km_pending, km_pending = 0 where player_token = '$token' and area_code = {$row->area_code}"); $added = 0; $expires = 0; } } else { $added = min($row->km_pending, $row->seconds * $travel_km_per_second); $row->km += $added; } } // error_log("KM: {$row->km} Finish line: $finish_line Reached: $row->reached"); if ($row->km >= $finish_line && !$row->reached) { $row->km = $finish_line; $row->reached = 1; $row->seconds_reached = 1; sql("update z_event_stats set reached = now(), km = $finish_line, km_pending = 0, km_update = now() where player_token = '$token' and area_code = {$row->area_code} and reached is null"); error_log("$token reached the finish line at event {$row->area_code}"); $expires = 0; } } printf("event_stats:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", (int)$row->area_code, (int)$row->rank, (int)$row->zombies_killed, (int)$row->km, $travel, (int)$row->km - ($total - $travel), $expires, $row->reached ? max(1, (int)$row->seconds_reached) : 0, (int)$row->laps_completed); } $areas = $active_areas; if (!$areas) $areas = "0"; else $areas .= ",0"; $battles = sql_multiple("select distinct z_event_battle_participants.battle_code, instance_code from z_event_battle_participants left join z_event_battle on z_event_battle.battle_code = z_event_battle_participants.battle_code where player_token = '$token' and prize_taken = 0 and area_code in ($areas) order by z_event_battle.expires desc"); while (($row = $battles->fetch_object()) != null) { if ($row->instance_code) { $participation = sql("select * from z_event_raid_instance_participants where instance_code = {$row->instance_code} and player_token = '$token'"); if (!$participation) { error_log("Instance participation for instance {$row->instance_code} not found for battle {$row->battle_code}"); continue; } $instance = sql("select * from z_event_raid_instances where code = {$row->instance_code}"); if (!$instance) { error_log("PHP Error: $token {$player->name} Instance {$row->instance_code} not found for battle {$row->battle_code}"); continue; } if ($instance->dismissed) { if ($instance->started_by == $token) { sql("update z_event_battle_participants set prize_taken = 1 where player_token = '$token' and battle_code = {$row->battle_code}"); error_log("$token {$player->name}'s own instance {$row->instance_code} dismissed, checking out battle {$row->battle_code}'s prize"); } else if ($instance->timestamp) { $elapsed = time() - strtotime($instance->timestamp); if ($elapsed > 86400) { // 24 hours sql("update z_event_battle_participants set prize_taken = 1 where player_token = '$token' and battle_code = {$row->battle_code}"); error_log("$token {$player->name} Old instance {$row->instance_code} ({$instance->timestamp}) dismissed for battle {$row->battle_code}"); } else { // error_log("$token {$player->name} Instance {$row->instance_code} dismissed for battle {$row->battle_code}, not removing yet ($elapsed elapsed)"); } } else { error_log("$token {$player->name} Instance {$row->instance_code} dismissed for battle {$row->battle_code}"); } continue; } } $b = dump_boss_battle($row->battle_code, "boss_battle", true); if ($b && $token !== $b->started_by) $starters[$b->started_by] = true; } $weapons_found = sql_multiple("select mission_code, weapon_1, weapon_2 from z_mission_weapon_found where player_token = '$token'"); $weapons = ""; while (($row = $weapons_found->fetch_object()) != null) { $weapons .= ":{$row->mission_code},{$row->weapon_1},{$row->weapon_2}"; } if ($weapons !== "") print("weapons_found" . $weapons . "\n"); $boss_recipes_unlocked = get_player_boss_recipes_unlocked($token); printf("boss_recipes_unlocked:%s\n", implode(":", $boss_recipes_unlocked)); $boss_captures = get_player_boss_captures($token); printf("boss_captures:%s\n", implode(":", array_map(function($r, $class) { return "$class,$r"; }, $boss_captures, array_keys($boss_captures)))); $gifts = sql_multiple("select * from z_gifts where player_token = '$token' order by category, date desc"); while (($row = $gifts->fetch_object()) != null) { printf("gift:code=%d:type=%s:value=%d:job=%s:category=%s:description=%s:sent_by=%s:shiny=%d:skin=%s\n", $row->code, $row->gift, $row->value, $row->job, $row->category, $row->category_description, $row->sent_by, $row->shiny, $row->skin); } $event_hints = sql_multiple("select area_code, hints_received, hint_active, time_to_sec(timediff(reset_in, now())) as hint_reset_seconds from z_event_gems_hint where player_token = '$token' and reset_in >= now() and area_code in (select code from z_mission_areas where event > 0 and hidden = 0)"); while (($row = $event_hints->fetch_object()) != null) printf("hints:%d:%d:%d:%d\n", $row->area_code, $row->hints_received, $row->hint_active, $row->hint_reset_seconds); dump_player_lust(); } function dump_player_lust($t = null) { global $token; if (!$t) $t = $token; // Lust progress $lust = []; $query = sql_multiple("select concat(girl, ',', level, ',', points) as lust from z_lust_progress where player_token = '$t'"); while (($row = $query->fetch_object()) != null) { $lust[] = $row->lust; } if ($lust) { echo "lust:" . implode(":", $lust) . "\n"; // error_log("Lust progress for $t: " . implode(":", $lust)); } } function dump_player_profile($token, $extras = "") { global $mysqli; $player = $token; if (is_string($token)) $player = get_player_profile($token); if ($player) { dump_profile($player, $extras); } else { printf("%s:UNKNOWN PLAYER\n", $token); } } $guilds = []; function dump_pvp_player_profile($token, $extras = "") { global $guilds; $player = $token; if (is_string($token)) $player = get_player_profile($token); if (strlen($player->token) > 0) { if ($player->guild) { if (!array_key_exists($player->guild, $guilds)) $guilds[$player->guild] = get_guild_profile($player->guild); $guild = $guilds[$player->guild]; $tag = preg_replace("/:/", "\\_", addcslashes($guild->tag, "\\")); dump_pvp_profile($player, ":guild_name={$guild->name}:guild_tag=$tag$extras"); } else { dump_pvp_profile($player, $extras); } } else { printf("%s:UNKNOWN PLAYER\n", $token); } } function next_hideout_value($part, $level) { $next = apcu_fetch("hideout_next_$part$level"); if ($next !== false) return $next; $next = sql("select value from z_hideout_levels where part = '$part' and level = $level + 1"); if (!$next) $next = 0; apcu_store("hideout_next_$part$level", $next); return $next; } function dump_player_hideout($token) { global $merc_slots; $merc_slots = 0; printf("hideout"); $results = sql_multiple("select z_hideout.*, value, level_required, upgrade_price, refill_price, friend_slots from z_hideout, z_hideout_levels where player_token = '$token' and z_hideout_levels.part = z_hideout.part and z_hideout_levels.level = z_hideout.level order by part"); while (($r = $results->fetch_object()) != null) { $r->next_value = next_hideout_value($r->part, $r->level); if ($r->part == "vehicle") { $merc_slots = $r->value; } $friends = ""; $friend_slots = (int)$r->friend_slots; /* -- Friend slots deprecated if ($friend_slots > 0) { $friends = array(); $friends_row = sql_multiple("select * from z_hideout_friends where player_token = '$token' and part = '$r->part'"); while (($row = $friends_row->fetch_object()) != null) { $friends[] = $row->slot . "#" . $row->friend_token; } $friends = " friends=" . join("/", $friends); } */ printf(":%s=%d value=%d/%d price=%d req=%d fill=%d friend_slots=%d%s", $r->part, $r->level, $r->value, $r->next_value, $r->upgrade_price, $r->level_required, (int)$r->refill_price, $friend_slots, $friends); } if (!isset($refill)) $refill = ""; printf(":$refill\n"); } function clear_mercs_cache($token) { apcu_delete("player_$token"); apcu_delete("mercs_$token"); apcu_delete("mercs_time_$token"); } function clear_inventory_cache($token) { apcu_delete("player_$token"); apcu_delete("inventory_$token"); apcu_delete("inventory_time_$token"); } function dump_player_mercs($token, $where = "") { $list = null; $extra_time = 0; if (!$where) { $list = apcu_fetch("mercs_$token"); $time = apcu_fetch("mercs_time_$token"); if ($list && $time) $extra_time = time() - $time; } if ($where) { $where = " and $where"; } if (!$list || true) { $time = time(); $extra_time = 0; $results = sql_multiple("select *, time_to_sec(timediff(now(), added)) as seconds, time_to_sec(timediff(boosted_until, now())) as boosted, time_to_sec(timediff(current_until, now())) as expires from z_mercs_inventory where player_token = '$token' $where order by sort_order"); if (!$results) { echo "mercs:\n"; return; } $list = []; while ($result = $results->fetch_object()) { if ($result->expires < 0) { update_mercs_stats($token, $result); } if ($result->current_energy == 0) { update_mercs_stats($token, $result); } $list[] = $result; } if (!$where) { apcu_store("mercs_$token", $list); apcu_store("mercs_time_$token", $time); } } $mercs = array(); foreach ($list as $result) { $merc_girl = $result->girl; $merc_attack = (int)$result->attack; $merc_defense = (int)$result->defense; $merc_xp = (int)$result->xp; $merc_order = (int)$result->sort_order; $merc_equipped = (int)$result->equipped_slot; $merc_seconds = (int)$result->seconds + $extra_time; $merc_boosted = (int)$result->boosted - $extra_time; $merc_shiny = (int)$result->shiny; $merc_busy = (int)$result->busy; $merc_energy = (int)$result->energy; $merc_rarity = (int)$result->rarity; $merc_evolution = (int)$result->evolution; $merc_skin = (int)$result->skin; $merc_count = (int)$result->count; $merc_evolutions = (int)$result->evolutions; $merc_prestige = (int)$result->prestige; $mission_code = (int)$result->mission_code; $battle_code = (int)$result->battle_code; $curr_attack = (double)$result->current_attack; $curr_defense = (double)$result->current_defense; $curr_energy = (int)$result->current_energy; $curr_boost = (int)$result->current_boost; $weapons = $result->weapons; if ($weapons == null) { update_mercs_stats($token, $result); error_log("$token Merc #$merc_order had no weapons, updated to {$result->weapons}"); $weapons = $result->weapons; } $merc_boost = 0; if ($merc_boosted > 0) { $merc_defense *= $result->boost; $merc_attack *= $result->boost; $merc_boost = $result->boost; } if ($merc_busy && $battle_code) { $battle = get_event_battle($battle_code); if (!$battle || $battle->health <= 0 || $battle->seconds < 0) { sql("update z_mercs_inventory set busy = 0, battle_code = null where player_token = '$token' and sort_order = $merc_order"); clear_mercs_cache($token); $battle_code = 0; $merc_busy = 0; } } elseif ($merc_busy && $mission_code) { $mission = sql("select *, finish_time <= now() as finished from z_in_progress where player_token = '$token' and merc = $merc_equipped"); if (!$mission) { sql("update z_mercs_inventory set busy = 0, mission_code = null where player_token = '$token' and sort_order = $merc_order"); clear_mercs_cache($token); $mission_code = 0; $merc_busy = 0; } } $mercs[] = "$merc_order,$merc_girl,$merc_attack,$merc_defense,". "$merc_xp,$merc_equipped,$merc_seconds,". "$merc_boosted,$merc_shiny,$merc_boost,$merc_busy,$merc_energy,$merc_rarity,". "$curr_attack,$curr_defense,$curr_energy,$merc_evolution,". "$battle_code,$mission_code,$merc_skin,$merc_count,$weapons,$curr_boost,$merc_evolutions,$merc_prestige"; } echo "mercs:" . implode(":", $mercs) . "\n"; } function dump_player_inventory($token) { $list = null; $extra_time = 0; $list = apcu_fetch("inventory_$token"); $time = apcu_fetch("inventory_time_$token"); if ($list && $time) $extra_time = time() - $time; if (!$list) { $time = time(); $extra_time = 0; $results = sql_multiple("select *, time_to_sec(timediff(now(), added)) as seconds, time_to_sec(timediff(boosted_until, now())) as boosted from z_inventory where player_token = '$token' order by sort_order"); $list = []; if (!$results) { error_log("PHP ERROR: No inventory found for $token"); } else { while ($result = $results->fetch_object()) $list[] = $result; // error_log("Inventory for $token: " . count($list) . " items"); apcu_store("inventory_$token", $list); apcu_store("inventory_time_$token", $time); } } $objects = array(); foreach ($list as $result) { $slot = (int)$result->slot; $object_code = (int)$result->object_code; $object_health = (int)$result->health; $object_energy = (int)$result->energy; $object_attack = (int)$result->attack; $object_defense = (int)$result->defense; $object_xp = (int)$result->xp; $object_order = (int)$result->sort_order; $object_equipped = (int)$result->equipped_slot; $object_seconds = (int)$result->seconds + $extra_time; $object_boosted = (int)$result->boosted - $extra_time; $object_shiny = (int)$result->shiny; $object_skill = (int)$result->skill; $object_skillxp = (int)$result->skill_xp; $object_skillec = (int)$result->skill_energy; $object_bookmark = (int)$result->bookmark; $object_count = (int)$result->count; $sb_unlocked = (int)$result->sb_unlocked; if (!$object_skill) $object_skill = 1000; $object_boost = 0; if ($object_boosted > 0) { $object_health *= $result->boost; $object_defense *= $result->boost; $object_attack *= $result->boost; $object_energy *= $result->boost; $object_boost = $result->boost; } else if ($result->boost) { $object = object_from($result); $object->boost = null; $object->boosted_until = null; update_inventory_object($token, $object); if ($object_equipped) { $merc_slot = floor($object_equipped / 10); update_mercs_stats($token, $merc_slot); } clear_inventory_cache($token); } if ($slot == 7) $slot = 6; if ($slot == 9) $slot = 2; $objects[$slot][] = "$object_code:$object_health:$object_energy:$object_attack:". "$object_defense:$object_xp:$object_order:$object_equipped:$object_seconds:". "$object_boosted:$object_shiny:$object_boost:". "$object_skill:$object_skillxp:$object_skillec:". "$object_bookmark:$object_count:$sb_unlocked"; } for ($n = 1 ; $n <= 6 ; $n++) { echo " "; if (array_key_exists($n, $objects)) echo implode(",", $objects[$n]) . "\n"; else echo "\n"; } } function dump_player_gunsmith($token) { echo " \n"; } function dump_player_achievements($token) { echo " \n"; } function dump_player_schematics($token) { echo " \n"; } query("select get_lock('$token', 0.25)"); if (!$result) return false; $value = $result->fetch_array(MYSQLI_NUM); $result->close(); if ($value && $value[0]) { // error_log("Locked $token"); $lock = true; } } } function unlock() { global $token, $mysqli, $lock; if(!$lock || !isset($mysqli->server_info)) return; $mysqli->query("select release_lock('$token')"); // error_log("Unlocked $token"); $lock = false; } // -------------------------------------------------------------------------------- // Objects and inventory // -------------------------------------------------------------------------------- function get_equipped_objects ($player_token) { $time = time(); $objects = apcu_fetch("equipped_objects_$player_token"); if ($objects) { foreach ($objects as $key => $object) { if ($objects[$key]->boosted) { $objects[$key]->boosted_seconds = ($object->time + $object->boosted_seconds) - $time; $objects[$key]->boosted = $objects[$key]->boosted_seconds > 0; } } return $objects; } $query = sql_multiple("select object_code, attack, defense, health, energy, equipped_slot, time_to_sec(timediff(boosted_until, now())) as boosted_seconds, boost from z_inventory where player_token = '$player_token' and equipped_slot is not null"); $objects = array(); while (($row = $query->fetch_object()) != null) { $row->time = $time; $row->boosted = $row->boosted_seconds > 0; $objects[] = $row; } apcu_store("equipped_objects_$player_token", $objects); return $objects; } function get_equipped_stats ($player_token, $materials = 0, $class_code = 0) { $equipped_objects = get_equipped_objects($player_token); $stats = (object)array( "attack" => 0, "defense" => 0, "energy" => 0, "health" => 0 ); // if ($object_class != 0) // $object_class = get_object_class($object_class); foreach ($equipped_objects as $equipped_object) { if ($equipped_object->equipped_slot > 6) continue; if ($materials != 0) { $info = get_object($equipped_object->object_code); if (($materials & (1 << ($info->material-1))) != 0) continue; } // if ($object_class && !$object_class[$object]) // continue; if ($equipped_object->boosted) { $stats->attack += $equipped_object->attack * $equipped_object->boost; $stats->defense += $equipped_object->defense * $equipped_object->boost; $stats->health += $equipped_object->health * $equipped_object->boost; $stats->energy += $equipped_object->energy * $equipped_object->boost; } else { $stats->attack += $equipped_object->attack; $stats->defense += $equipped_object->defense; $stats->health += $equipped_object->health; $stats->energy += $equipped_object->energy; } } return $stats; } function get_pack ($pack) { global $token; if (is_numeric($pack)) { $pack = sql("select *, ifnull(expires,now()) > now() as expired from z_object_packs where code = $pack"); } $code = $pack->code; if (!isset($pack->expires_seconds)) { if ($pack->expires) { $pack->expires_seconds = sql("select time_to_sec(timediff(expires, now())) from z_object_packs where code = $code"); if ($pack->expires_seconds < 0) { $pack->expires_seconds = 0; } } else { $pack->expires_seconds = 0; // No expiration } } if ($pack->area_code) { $area_expires_seconds = sql("select time_to_sec(timediff(expires, now())) from z_mission_areas where code = $pack->area_code"); if (!$pack->expires_seconds || $pack->expires_seconds > $area_expires_seconds) { $pack->expires_seconds = $area_expires_seconds; } if ($pack->rotation_step) { $remaining = get_area_seconds_until_next_rotation($pack->area_code); if ($pack->expires_seconds > $remaining || !$pack->expires_seconds) { $pack->expires_seconds = $remaining; } } } if ($pack->duration) { $expires = sql("select time_to_sec(timediff(expiration, now())) from z_player_pack_expiration where player_token = '$token' and pack_code = $code"); if ($expires === null) { sql("insert ignore into z_player_pack_expiration (player_token, pack_code, expiration) values ('$token', $code, date_add(now(), interval $pack->duration second)"); $expires = $pack->duration; } if (!$pack->expires_seconds || $expires < $pack->expires_seconds) { $pack->expires_seconds = $expires; } } $pack->time = (int)sql("select sum(count) from z_purchased_packs where player_token = '$token' and code = $pack->code") + 1; // error_log("$token Pack $pack->code expires in $pack->expires_seconds seconds, time: $pack->time"); if ($pack->guaranteed_weapon_times || $pack->guaranteed_girl_times) { $weapon_times = explode(",", $pack->guaranteed_weapon_times); $girl_times = explode(",", $pack->guaranteed_girl_times); $guaranteed_weapon = in_array("$pack->time", $weapon_times); $guaranteed_girl = in_array("$pack->time", $girl_times); if ($guaranteed_weapon || $guaranteed_girl) { $pack->overlay = "offer"; $pack->overlay_text = "This time|SL Guaranteed"; } $pack->highlight_1 = $guaranteed_weapon ? "SLegendary Weapon Guaranteed" : "SLegendary Weapon possible"; $pack->highlight_2 = $guaranteed_girl ? "SLegendary Merc Guaranteed" : "SLegendary Merc possible"; } if ($pack->shiny_min_purchases <= $pack->time && $pack->shiny_chance) { $pack->overlay = "offer"; $pack->overlay_text = "This time|" . ($pack->shiny_chance) . "x Shiny %"; } if ($pack->first_time_offer && $pack->time == 1) { $pack->price = floor($pack->price * $pack->first_time_offer); $pack->overlay = "first_time"; $pack->overlay_text = "First Time|" . (100 - $pack->first_time_offer * 100) . "% Off"; } if (!@$pack->objects_list || @$pack->girl) { $total_count = 0; $detail = []; $object_chances = []; $pools = []; $query = sql_multiple("select pool_code, count from z_object_pack_list where pack_code = {$pack->code}"); while (($row = $query->fetch_object()) != null) { $pools[] = $row; } if ($pack->pool && $pack->rolls) { $guaranteed_girl_times = explode(",", $pack->guaranteed_girl_times); $guaranteed_weapon_times = explode(",", $pack->guaranteed_weapon_times); $current_time = (string)$pack->time; $pool = (object) [ 'pool_code' => $pack->pool, 'count' => $pack->rolls ]; if (in_array($current_time, $guaranteed_girl_times)) { $pools[] = (object) [ 'pool_code' => -2, // Guaranteed merc pool 'count' => 1 ]; $pool->count--; } if (in_array($current_time, $guaranteed_weapon_times)) { $pools[] = (object) [ 'pool_code' => -1, // Guaranteed weapon pool 'count' => 1 ]; $pool->count--; } $pools[] = $pool; } $pack->pools = $pools; $pack->class_objects = []; foreach ($pools as $pool) { $pool_name = sql("select description from z_object_pools where code = $pool->pool_code"); $total_count += $pool->count; $classes = []; // error_log("Processing pool $pool->pool_code ($pool_name) with {$pool->count} rolls"); $query = sql_multiple("select class_code, chance, girl from z_object_pool_list where pool_code = $pool->pool_code"); $total = 0; $girls_total = 0; $girls = 0; while ($row = $query->fetch_object()) { if ($row->girl) { $level = sql("select level from z_lust_progress where player_token = '$token' and girl = '$row->girl'"); if ($level) { $row->chance = $level; } else { $level = 1; } $girls++; $girls_total += $level; } else { $total += (float)$row->chance; } $classes[] = $row; } $factor = $total > 0 ? (100 - $girls_total)/$total : 0; // error_log("Factor: $factor, $girls_total in girls, $total points in total, $girls girls"); $pool_detail = []; foreach ($classes as $class) { $class_objects = []; $class->chance = (float)$class->chance; $visibleChance = sprintf("%.02f", $class->chance); // error_log("Class {$class->class_code} chance: $visibleChance, factor: $factor"); if ($class->class_code == -1) { // Guaranteed weapon from the pack's guaranteed weapons list $weapons = explode(",", $pack->guaranteed_weapons); $names = []; foreach ($weapons as $weapon) { $object = get_object($weapon); if (!$object) { error_log("PHP ERROR: Invalid code $weapon in guaranteed weapons list for pack $code"); continue; } $class_objects[] = $weapon; $names[] = $object->name; } // Find the common prefix to all names $prefix = ""; if (count($names) > 0) { $prefix = $names[0]; for ($i = 1; $i < count($names); $i++) { $name = $names[$i]; while (strpos($name, $prefix) !== 0) { $prefix = substr($prefix, 0, -1); if ($prefix == "") break; } } $prefix = preg_replace('/[\s(]+$/', '', $prefix); // Remove trailing spaces } if ($prefix) $pool_detail[] = "$visibleChance% $prefix"; else $pool_detail[] = "$visibleChance% SL weapon"; } else if ($class->class_code == -2) { // Guaranteed merc from the pack's guaranteed girls list $mercs = explode(",", $pack->guaranteed_girls); $names = []; foreach ($mercs as $merc) { $merc = sql("select * from z_lust_girls where id = '$merc'"); if (!$merc) { error_log("PHP ERROR: Invalid code $merc in guaranteed girls list for pack $code"); continue; } $class_objects[] = $merc->code; $names[] = $merc->name; } if (count($names) == 1) { $pool_detail[] = "$visibleChance% {$names[0]}"; } else { $pool_detail[] = "$visibleChance% SL merc"; } } else { $class_name = sql("select description from z_object_classes where code = $class->class_code"); if (!$class->girl) $class->chance *= $factor; if (str_ends_with($visibleChance, ".00")) { $visibleChance = substr($visibleChance, 0, -3); } $pool_detail[] = trim("$visibleChance% $class_name"); $objects = sql_multiple("select * from z_object_class_list where class_code = $class->class_code"); while (($object = $objects->fetch_object()) != null) { if ($object->skin) $class_objects[] = "$object->object_code/$object->skin"; else $class_objects[] = $object->object_code; } } if (count($class_objects) == 0) { error_log("PHP ERROR: No objects in class $class->class_code"); continue; } $base_chance = $class->chance / count($class_objects); $unit_chance = 0; for ($n = 0; $n < $pool->count; $n++) { $unit_chance += (100 - $unit_chance) * $base_chance / 100; } foreach ($class_objects as $object) { if (array_key_exists($object, $object_chances)) $object_chances[$object] += (100 - $object_chances[$object]) * $unit_chance / 100; else $object_chances[$object] = $unit_chance; } $pack->class_objects[$class->class_code] = $class_objects; } $detail[] = "$pool_name -> {$pool->count}x" . join(",", $pool_detail); } $objects_list = array_keys($object_chances); $objects_list = join(",", $objects_list); $pack->objects_list = $objects_list; $chances = join("#", $detail); $pack->chances = $chances; } return $pack; } function get_payoff_spent_field ($payoff) { $field = "money"; if (strpos($payoff, "jingle") !== false) { $field = "jingle_bells_spent"; } else if (strpos($payoff, "tomoe") !== false) { $field = "tomoe_coins_spent"; } else if (strpos($payoff, "black") !== false) { $field = "black_jewels_spent"; } else if (strpos($payoff, "halloween") !== false) { $field = "deadly_coins_spent"; } else if (strpos($payoff, "mimic") !== false) { $field = "mimics_killed"; } else if (strpos($payoff, "zombies") !== false) { $field = "zombies"; } return $field; } $game_variables = []; function get_game_variable ($name) { global $game_variables; if (array_key_exists($name, $game_variables)) return $game_variables[$name]; $value = apcu_fetch("variable_$name"); if ($value != false) { // error_log("Game variable $name in cache: $value"); $game_variables[$name] = $value; return $value; } $value = sql("select value from z_game_variables where variable = '$name'"); // error_log("Game variable $name from database: $value"); apcu_store("variable_$name", $value); $game_variables[$name] = $value; return $value; } function clear_game_variable_cache ($name) { apcu_delete("variable_$name"); } function get_lust_girl($id) { $value = apcu_fetch("lust_girl_$id"); if ($value != false) return $value; $value = sql("select * from z_lust_girls where id = '$id'"); apcu_store("lust_girl_$id", $value); return $value; } function get_skin_tag($skin) { $value = apcu_fetch("skin_tag_$skin"); if ($value != false) return $value; $value = sql("select tag from z_skins where id = $skin"); apcu_store("skin_tag_$skin", $value); return $value; } function in_tutorial() { global $token; $player = get_player_profile($token); return $player->level < get_game_variable("event_unlock_level"); } function get_active_event($in_tutorial = null) { if ($in_tutorial === null) $in_tutorial = in_tutorial(); $in_tutorial = ($in_tutorial ? "1" : "0"); $value = apcu_fetch("active_event_$in_tutorial"); if ($value != false) return $value; $value = (int)sql("select code from z_mission_areas where event > 0 and hidden = 0 and expires >= now() and (tutorial is null or tutorial = $in_tutorial)"); apcu_store("active_event", $value, 60); return $value; } function get_active_race($in_tutorial = null) { if ($in_tutorial === null) $in_tutorial = in_tutorial(); $in_tutorial = ($in_tutorial ? "1" : "0"); $value = apcu_fetch("active_race_$in_tutorial"); if ($value != false) return $value; $value = (int)sql("select code from z_mission_areas where event = 2 and hidden = 0 and expires >= now() and (tutorial is null or tutorial = $in_tutorial)"); apcu_store("active_race_$in_tutorial", $value, 60); return $value; } function get_active_areas($in_tutorial = null) { if ($in_tutorial === null) $in_tutorial = in_tutorial(); $in_tutorial = ($in_tutorial ? "1" : "0"); global $token; $player_hex = hexdec(substr($token, 0, 1)); $key = $in_tutorial ? "active_areas_tutorial_$player_hex" : "active_areas"; $value = apcu_fetch($key); if ($value != false) { if (!$value) $value = -1; return $value; } $value = null; $query = sql_multiple("select code, hex from z_mission_areas where event > 0 and hidden = 0 and (expires >= now() or expires is null) and (tutorial is null or tutorial = $in_tutorial) order by code"); while (($row = $query->fetch_object()) != null) { if ($row->hex) { $hex_from = hexdec(substr($row->hex, 0, 1)); $hex_to = hexdec(substr($row->hex, 2, 1)); if ($player_hex < $hex_from || $player_hex > $hex_to) continue; } $value = $row->code; break; } if (!$value) $value = -1; apcu_store($key, $value, 60); return $value; } function get_event_features($in_tutorial = null) { $value = apcu_fetch("event_features"); if ($value != false) return $value; $active_areas = get_active_areas($in_tutorial); $features = []; $query = sql_multiple("select * from z_event_features where area_code in ($active_areas)"); while (($row = $query->fetch_object()) != null) { $row->active = 1; if ($row->rotation) { $active_rotation = get_area_rotation($row->area_code); if ($active_rotation != $row->rotation) $row->active = 0;; } $features[] = $row; } apcu_store("event_features", $features, 60); return $features; } $area_rotation_cache = []; function get_active_area_rotation() { $area_code = get_active_event(); if (!$area_code) return 0; return get_area_rotation($area_code); } function get_area_rotation($area) { global $area_rotation_cache; if (array_key_exists($area, $area_rotation_cache)) return $area_rotation_cache[$area]; $value = apcu_fetch("area_rotation_$area"); if ($value != false) return $value; $value = 0; $area = sql("select * from z_mission_areas where code = $area"); if ($area && $area->released) { if (!$area->started) { sql("update z_mission_areas set started = now() where code = $area->code"); } $elapsed_hours = sql("select time_to_sec(timediff(now(), started))/3600 as elapsed from z_mission_areas where code = $area->code"); if (!$area->rotation_hours) $value = 0; else $value = (int)($elapsed_hours / $area->rotation_hours) + 1; } apcu_store("area_rotation_{$area->code}", $value, 60); $area_rotation_cache[$area->code] = $value; return $value; } function get_area_seconds_until_next_rotation($area) { if (is_numeric($area)) $area = sql("select * from z_mission_areas where code = $area"); if (!$area || !$area->rotation_hours) return 0; $elapsed_hours = sql("select time_to_sec(timediff(now(), started))/3600 as elapsed from z_mission_areas where code = $area->code"); $next_rotation = $area->rotation_hours - fmod($elapsed_hours, $area->rotation_hours); $result = floor($next_rotation * 3600); // error_log("Area $area->code: next rotation in $result seconds ($next_rotation hours, $elapsed_hours elapsed)"); return $result; } function get_service ($code) { $service = apcu_fetch("service_$code"); if ($service != false) return $service; $service = sql("select * from z_services where code = $code"); apcu_store("service_$code", $service); return $service; } function get_object ($code) { $object = apcu_fetch("object_$code"); if ($object != false) return $object; $code = (int)$code; if (!$code) { error_log("PHP ERROR: Invalid object code $code requested"); error_log("Debug backtrace: " . debug_backtrace()); } $object = sql("select * from z_objects where code = $code"); apcu_store("object_$code", $object); return $object; } function get_consumable ($code) { $consumable = apcu_fetch("consumable_$code"); if ($consumable != false) return $consumable; $consumable = sql("select * from z_consumables where code = $code"); apcu_store("consumable_$code", $consumable); return $consumable; } function get_skill ($code) { $skill = apcu_fetch("skill_$code"); if ($skill) return $skill; if (!$code) $code = 1000; $skill = sql("select * from z_skills where code = $code"); if ($skill) apcu_store("skill_$code", $skill); return $skill; } function get_boss_skill ($code) { $skill = apcu_fetch("boss_skill_$code"); if ($skill) return $skill; $skill = sql("select * from z_boss_skills where code = $code"); apcu_store("boss_skill_$code", $skill); return $skill; } function get_object_class ($class_code) { $class = apcu_fetch("object_class_$class_code"); if ($class) return $class; $class = array(); $sql = sql_multiple("select * from z_object_class_list where class_code = $class_code"); while (($row = $sql->fetch_object()) != null) $class[$row->object_code] = true; apcu_store("object_class_$class_code", $class); return $class; } // -------------------------------------------------------------------------------- // Event battles // -------------------------------------------------------------------------------- function get_boss ($area_code, $instance_code, $class, $level) { $boss = null; if ($instance_code) $boss = apcu_fetch("boss_{$area_code}_{$instance_code}_{$class}_{$level}"); else $boss = apcu_fetch("boss_{$area_code}_{$class}_{$level}"); if ($boss) { return $boss; } if ($instance_code) { $boss = sql("select * from z_event_raid_instance_boss where instance_code = $instance_code and level = $level"); if (!$boss) { $instance_code = null; $boss = apcu_fetch("boss_{$area_code}_{$class}_{$level}"); } } if (!$boss) { $boss = sql("select * from z_event_boss where (area_code = $area_code or area_code = 0) and class = $class and level = $level"); if ($boss) { $boss->code = 0; } } if (!$boss) { $level_info = sql("select * from z_event_boss_levels where level = $level"); if (!$level_info) { error_log("PHP ERROR: No boss level $level in table"); $level_info = sql("select * from z_event_boss_levels order by level desc limit 1"); $level_info->level = $level; } $boss = sql("select * from z_event_boss where (area_code = $area_code or area_code = 0) and class = $class and level <= $level order by level desc"); if (!$boss) { error_log("PHP Error: no boss found for class $class level $level in area $area_code"); error_log("select * from z_event_boss where (area_code = $area_code or area_code = 0) and class = $class and level <= $level order by level desc"); return; } if ($boss->level > 1) { $prev_info = sql("select * from z_event_boss_levels where level = {$boss->level}"); $boss->health /= $prev_info->health; $boss->attack /= $prev_info->attack; $boss->defense /= $prev_info->defense; $boss->zombies /= $prev_info->zombies; } $boss->health *= $level_info->health; $boss->attack *= $level_info->attack; $boss->defense *= $level_info->defense; $boss->zombies *= $level_info->zombies; $boss->level = $level_info->level; } if (isset($boss->skills)) { $boss->skills = preg_replace('/, +/', ',', $boss->skills); } // error_log("Boss for class $class level $level in area $area_code: {$boss->zombies} zombies (x{$level_info->zombies})"); if (!isset($boss->class)) $boss->class = 0; if ($instance_code) apcu_store("boss_{$area_code}_{$instance_code}_{$class}_{$level}", $boss); else apcu_store("boss_{$area_code}_{$class}_{$level}", $boss); // error_log("Boss: {$boss->class} {$boss->name} {$boss->sprite} " . print_r($boss, true)); return $boss; } function apply_battle_effects (&$battle) { if ($battle->poison && $battle->health > 0 && $battle->poisoned > 0) { $isnull = sql("select 1 from z_event_battle where battle_code = {$battle->battle_code} and (poison_time is null or poison_time = '0000-00-00 00:00:00')"); if ($isnull) { sql("update z_event_battle set poison_time = now() where battle_code = {$battle->battle_code}"); $battle->poisoned = 0; } $inc = floor($battle->poisoned/10); if ($inc > 0) { $damage = $inc * $battle->poison; error_log("Poison: {$battle->poison} damage x $inc ticks ({$battle->poisoned}s), {$damage} damage"); $battle->health -= $damage; if ($battle->health <= 0) $battle->health = 0; $seconds = $inc * 10; sql("update z_event_battle set health = {$battle->health}, poison_time = date_add(ifnull(poison_time, now()), interval $seconds second) where battle_code = {$battle->battle_code}"); clear_event_battle_cache($battle->battle_code); } } if ($battle->regeneration && $battle->health > 0) { $increment = floor($battle->elapsed/10) * $battle->regeneration; // error_log("Regeneration: {$battle->regeneration} reg x {$battle->elapsed} seconds -> {$increment} health"); $battle->health += $increment; if ($battle->health > $battle->max_health) $battle->health = $battle->max_health; } } function get_event_battle ($battle_code) { if (!$battle_code) return null; $time = time(); $battle = apcu_fetch("event_battle_$battle_code"); if ($battle) { $extra_time = $time - $battle->time; $battle->seconds -= $extra_time; $battle->elapsed += $extra_time; $battle->started += $extra_time; $battle->poisoned += $extra_time; if ($battle->seconds <= 0) $battle->expired = 1; apply_battle_effects($battle); return $battle; } $battle = sql("select *, time_to_sec(timediff(expires, now())) seconds, time_to_sec(timediff(expires, started)) total, time_to_sec(timediff(now(), updated)) elapsed, time_to_sec(timediff(now(), started)) started, time_to_sec(timediff(now(), poison_time)) poisoned, expires <= now() expired from z_event_battle where battle_code = $battle_code"); if (!$battle) return; $battle->time = $time; apcu_store("event_battle_$battle_code", $battle); apply_battle_effects($battle); return $battle; } function clear_event_battle_cache($battle_code) { apcu_delete("event_battle_$battle_code"); } // -------------------------------------------------------------------------------- // Hideout information and cache // -------------------------------------------------------------------------------- function get_hideout_profile ($token) { $hideout = apcu_fetch("hideout_$token"); if ($hideout) return (object)$hideout; $house_level = sql("select level from z_hideout where player_token = '$token' and part = 'house'"); $security_level = sql("select level from z_hideout where player_token = '$token' and part = 'security'"); $vehicle_level = sql("select level from z_hideout where player_token = '$token' and part = 'vehicle'"); $wall_level = sql("select level from z_hideout where player_token = '$token' and part = 'wall'"); $hideout = (object) array( "mercs" => (int)sql("select value from z_hideout_levels where part = 'vehicle' and level = $vehicle_level"), "max_friends" => (int)sql("select value from z_hideout_levels where part = 'house' and level = $house_level"), "money_max" => (int)sql("select value from z_hideout_levels where part = 'security' and level = $security_level"), "wall_bonus" => (int)sql("select value from z_hideout_levels where part = 'wall' and level = $wall_level"), "vehicle_bonus" => (int)sql("select value from z_hideout_levels where part = 'vehicle' and level = $vehicle_level"), "wall_recruits" => (int)sql("select sum(1+(slot = 0)) from z_hideout_friends where player_token = '$token' and part = 'wall'"), "vehicle_recruits" => (int)sql("select sum(1+(slot = 0)) from z_hideout_friends where player_token = '$token' and part = 'vehicle'") ); // error_log("Hideout: " . json_encode($hideout)); apcu_store("hideout_$token", $hideout); return $hideout; } function clear_hideout_cache($token) { apcu_delete("hideout_$token"); } // -------------------------------------------------------------------------------- // Help requests // -------------------------------------------------------------------------------- function get_help_requests_to($token) { $requests = apcu_fetch("help_friend_$token"); $time = apcu_fetch("help_time_friend_$token"); if ($requests && $time) { $extra_seconds = time() - $time; foreach ($requests as $request) { $request->elapsed += $extra_seconds; } return $requests; } $query = sql_multiple("select *, time_to_sec(timediff(now(), date)) as elapsed from z_help_requests where friend_token = '$token'"); $requests = []; while (($row = $query->fetch_object()) != null) $requests[] = $row; apcu_store("help_friend_$token", $requests); apcu_store("help_time_friend_$token", time()); return $requests; } function get_help_requests($token) { $requests = apcu_fetch("help_$token"); $time = apcu_fetch("help_time_$token"); if ($requests && $time) { $extra_seconds = time() - $time; foreach ($requests as $request) { $request->elapsed += $extra_seconds; } return $requests; } $requests = []; $query = sql_multiple("select *, time_to_sec(timediff(now(), date)) as elapsed from z_help_requests where player_token = '$token'"); while (($row = $query->fetch_object()) != null) $requests[] = $row; apcu_store("help_$token", $requests); apcu_store("help_time_$token", time()); return $requests; } function clear_help_cache($token) { apcu_delete("help_$token"); apcu_delete("help_friend_$token"); } // -------------------------------------------------------------------------------- // Player friends & nearby players // -------------------------------------------------------------------------------- function get_player_friends ($token, $minLevel = 1) { $friends = []; $query = sql_multiple("select friend_token from z_friends where player_token = '$token'"); while (($row = $query->fetch_object()) != null) { $level = sql("select level from z_players where token = '$row->friend_token'"); if ($level && $level >= $minLevel) $friends[] = $row->friend_token; } return $friends; } function get_players_available ($server, $area_code, $count, $minLevel) { global $token; if (!$minLevel) { $minLevel = 3; } error_log("Finding up to $count players in area $area_code with level >= $minLevel"); $query = sql_multiple("select token, name from z_players where last_access >= date_sub(now(), interval 5 minute) and level >= $minLevel and token != '$token' and server = $server order by rand() limit $count"); $players = array(); $names = array(); $total = 0; while (($row = $query->fetch_object()) != null) { error_log("Player $row->token $row->name is available"); $players[] = $row->token; $names[] = "$row->token $row->name"; $total++; } error_log("Finding up to $count players in area $area_code. $total Found ".join(",", $names)); return $players; } // -------------------------------------------------------------------------------- // Player-related utility functions // -------------------------------------------------------------------------------- function get_skill_level($level) { $cached = apcu_fetch("skill_level_$level"); if ($cached) return $cached; $result = sql("select * from z_skill_levels where level = $level"); apcu_store("skill_level_$level", $result); return $result; } function get_player_level($level) { $cached = apcu_fetch("level_$level"); if ($cached) return $cached; $result = sql("select * from z_levels where level = $level"); apcu_store("level_$level", $result); return $result; } function get_player_name($token) { $cached = apcu_fetch("player_$token"); if ($cached) return $cached->name; $value = apcu_fetch("player_name_$token"); if ($value) return $value; $result = sql("select name from z_players where token = '$token'"); apcu_store("player_name_$token", $result); return $result; } function get_player_guild($token) { if (substr($token,0,1) == "G") return null; $cached = apcu_fetch("player_$token"); if ($cached) return $cached->guild; return sql("select guild from z_players where token = '$token'"); } function clear_player_cache($token) { global $profile_cache; if (!$token) return; apcu_delete("player_$token"); apcu_delete("level_$token"); apcu_delete("equipped_objects_$token"); apcu_delete("friends_$token"); apcu_delete("boss_recipes_unlocked_$token"); if ($profile_cache && array_key_exists($token, $profile_cache)) $profile_cache[$token] = null; } function clear_capture_cache($token) { apcu_delete("boss_captures_$token"); } function get_player_boss_recipes_unlocked($token) { $cached = apcu_fetch("boss_recipes_unlocked_$token"); if ($cached != false) return $cached; $result = []; $query = sql_multiple("select * from z_boss_recipes_unlocked where player_token = '$token'"); while (($row = $query->fetch_object()) != null) $result[] = $row->raid; apcu_store("boss_recipes_unlocked_$token", $result); return $result; } function get_player_boss_captures($token) { $cached = apcu_fetch("boss_captures_$token"); if ($cached != false) return $cached; $result = []; $query = sql_multiple("select * from z_player_captures where player_token = '$token'"); while (($row = $query->fetch_object()) != null) $result[$row->boss_class] = (int)$row->count; apcu_store("boss_captures_$token", $result); return $result; } /** Creates a new player with the default equipment in objects and hideout, * and updates the countries tables with the new stats. **/ function create_player($name, $country) { global $token; global $mysqli; $prefix = sprintf("%04X", mt_rand(0,65536)) . sprintf("%04X", mt_rand(0,65536)); $token = uniqid($prefix."_", true); $level = get_player_level(1); $energy = (int)$level->energy; $health = (int)$level->health; error_log("Creating new player: $token $name $country"); $name = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $name); if (!$name) { error_log("PHP Error: Empty name $name"); $name = "Player"; } $name = iconv("ISO-8859-1", "UTF-8", $name); $name = $mysqli->real_escape_string($name); $server = 0; $pvp_slot = (int)sql("select ifnull(max(pvp_slot),0) from z_players where level = 1 and server = $server") + 1; sql("insert into z_players (token, name, level, energy, energy_max, health, health_max, money, money_max, jewels, xp, xp_prev, xp_next, attack, defense, next_order, zombies, energy_time, country, male, head, health_cost, energy_cost, pvp_slot, pvp_weight, pvp_handicap, health_time, pvp_wins, pvp_loses, mercs, last_attack, created, elo, server) values ('$token', '$name', 1, $energy, $energy, $health, $health, 2500, 5000, 10, 0, 0, 4, 10, 10, 3, 0, null, '$country', 1, 1, 0, 0, $pvp_slot,0, 100, null, 0, 0, 0, null, now(), 300, $server)"); sql("insert into z_players_creation values ('$token', now())"); sql("update z_server_countries set players = players + 1 where country = '$country' and server = $server"); /* HACK for Nutaku testing */ $sandbox = getenv("SANDBOX") ?? false; if ($sandbox) { sql("update z_players set developer = 1 where token = '$token'"); } /* Default merc */ $query = sql_multiple("select hex, illustration from z_mission_areas where tutorial = 1"); $player_hex = hexdec(substr($token, 0, 1)); while (($row = $query->fetch_object()) != null) { $hex_from = hexdec(substr($row->hex, 0, 1)); $hex_to = hexdec(substr($row->hex, 2, 1)); if ($player_hex >= $hex_from && $player_hex <= $hex_to) { $illustration = str_replace("*", "", $row->illustration); $merc = sql("select * from z_lust_girls where id = '$illustration'"); if (!$merc) { error_log("PHP ERROR: No mercenary $illustration for player hex " . substr($token, 0, 1)); } } } if (!$merc) { error_log("PHP ERROR: No mercenary found for player hex " . substr($token, 0, 1)); $merc = sql("select * from z_lust_girls where id = 'merc_IE'"); } if ($merc) { make_shiny($merc, 100); add_inventory_merc($token, new InventoryMerc( player_token: $token, girl: $merc->id, attack: $merc->attack, defense: $merc->defense, energy: $merc->energy, rarity: $merc->rarity, shiny: 1, evolutions: $merc->evolutions )); } /* Default hideout values */ sql("insert into z_hideout values ". "('$token', 'house', 1), ". "('$token', 'security', 1), ". "('$token', 'vehicle', 1), ". "('$token', 'wall', 1)"); error_log("New player created: $token $name"); update_player_stats($token); } /** Updates a player profile in-memory with the energy and health * adjusted for the time passed since the table was last updated * (considering +1 each 10 seconds for each). * * Returns the updated player profile **/ function update_profile($player) { if ($player == null) { error_log("PHP ERROR: Null player in update_profile"); return null; } if ($player->energy < $player->energy_max) { $base_increment = $player->energy_max / 10800.0; if ($player->vip == 2 && $player->vip_active) { $base_increment *= 1.1; } if ($base_increment < 0) $base_increment = 0; $elapsed = (int)$player->energy_elapsed_time; $increment = (int)($elapsed * $base_increment); $player->energy = min($player->energy_max, $player->energy + $increment); $player->energy_seconds = ($player->energy_max - $player->energy) / $base_increment; $player->energy_elapsed_time = 0; } if ($player->health < $player->health_max) { $base_increment = $player->health_max / 3600; if ($base_increment < 0) $base_increment = 0; $elapsed = (int)$player->health_elapsed_time; $increment = (int)($elapsed * $base_increment); $player->health = min($player->health_max, $player->health + $increment); $player->health_seconds = ($player->health_max - $player->health) / $base_increment; $player->health_elapsed_time = 0; } return $player; } /** Recovers a player profile from the players table and returns it, * with health and energy updated for the current time. **/ function get_player_profile($token) { global $profile_cache; if (!$profile_cache) $profile_cache = array(); $cached = null; if (is_string($token)) { if (array_key_exists($token, $profile_cache)) $cached = $profile_cache[$token]; else $cached = apcu_fetch("player_$token"); if ($cached && $cached->time) { $extra_time = time() - $cached->time; @$cached->energy_elapsed_time += $extra_time; @$cached->health_elapsed_time += $extra_time; @$cached->shield_seconds -= $extra_time; @$cached->rage_seconds -= $extra_time; @$cached->zombie_multiplier_seconds -= $extra_time; $player = update_profile($cached); return $player; } $player = sql("select *, ". "time_to_sec(timediff(now(), energy_time)) as energy_elapsed_time, ". "time_to_sec(timediff(now(), health_time)) as health_elapsed_time, ". "time_to_sec(timediff(shield_expires, now())) as shield_seconds, ". "time_to_sec(timediff(shield_expires, shield_bought)) as shield, ". "time_to_sec(timediff(rage_expires, now())) as rage_seconds, ". "time_to_sec(timediff(zombie_multiplier_expires, now())) as zombie_multiplier_seconds, ". "(vip_1_end > now() or vip_2_end > now()) as vip_active, ". "(date(last_jewels_purchase) = curdate()) as jewels_purchased_today ". "from z_players where token = '$token'"); if ($player) { $player->time = time(); $profile_cache[$token] = $player; update_player_cache($player); $player = update_profile($player); } return $player; } return $token; } function update_player_cache($player) { if ($player && $player->token) { global $profile_cache; $player->time = time(); $profile_cache[$player->token] = $player; apcu_store("player_{$player->token}", $player); } } /** Updates a player's stats in the players table. This has the * following effects: * * - Energy and health are updated considering the time passed * since the last player update (+1 each 10 seconds) * - Attack and defense are recalculated considering the objects * currently equipped and the current hideout levels * - Maximum amount of money, health and energy are recalculated * considering the player level and objects equipped **/ function update_player_stats($token) { global $_SERVER; if (!is_string($token)) { $player = $token; $token = $player->token; } else { $player = get_player_profile($token); if (!$player) { error_log("PHP ERROR: Unable to update non-existing player $token"); return $player; } $player = update_profile($player); } if (!$player) { error_log("PHP ERROR: Unable to update player $token"); return $player; } $hideout = get_hideout_profile($token); $base = get_player_level($player->level); $max_friends = $hideout->max_friends; $money_max = $hideout->money_max; $health_cost = $base->health_cost; $energy_cost = $base->energy_cost; $energy = $base->energy; $health = $base->health; $attack = $base->attack; $defense = $base->defense; if ($energy > 9999) $energy = 9999; if ($health > 9999) $health = 9999; $new_health = min($health, $player->health); $new_energy = min($energy, $player->energy); $user_agent = @$_SERVER['HTTP_USER_AGENT']; if (str_contains($user_agent, "Android")) $platform = "android"; else $platform = "web"; sql("update z_players set energy_max = $energy, attack = $attack, defense = $defense, money_max = $money_max, energy_cost = $energy_cost, health_cost = $health_cost, max_friends = $max_friends, health_max = $health, last_access = now(), from_$platform = true, platform = '$platform', health = $new_health, health_time = now(), energy = $new_energy, energy_time = now() where token = '$token'"); $player->health_max = $health; $player->energy_max = $energy; $player->health = min($health, $player->health); $player->energy = min($energy, $player->energy); $player->attack = $attack; $player->defense = $defense; $player->time = time(); update_player_cache($player); return $player; } function compare_player_weights($a, $b) { return $b->pvp_weight - $a->pvp_weight; } function set_player_level($player, $new_level) { sql("update z_players set level = $new_level where token = '{$player->token}'"); } /** Adds the given amount of experience to a player. This may incurr in a * level change, in which case, the rest of player stats (energy, etc.) * are recalculated using update_player_stats() **/ function add_experience($player, $xp, $guild = null) { global $job_bonus, $token; if (is_string($player)) $player = get_player_profile($player); error_log("{$player->token} {$player->name} (level {$player->level}) gains $xp experience points"); if ($player->guild && $guild == null) $guild = get_guild_profile($player->guild); if ($guild) { $bonus = $job_bonus[$player->guild_job] * $guild->members + $guild->extra_xp / 100; $pr = $xp; $xp = floor($xp + $bonus*$xp); error_log("{$player->token} {$player->name} Gets a x$bonus experience bonus due to guild ($pr -> $xp XP)"); } $increment = $xp; /* Do not adjust energy! */ $level = $player->level; $prevlev = $level; $prevxp = $player->xp; $xp_prev = $player->xp_prev; $xp_next = $player->xp_next; $xp = $player->xp + $xp; $basexp = $xp; $health = $player->health; $health_max = $player->health_max; $energy = $player->energy; $energy_max = $player->energy_max; while ($xp < $xp_prev) { $prev = sql("select * from z_levels where level = " . ($level - 1)); if (!$prev) { $xp = $xp_prev; break; } else { $level = $level-1; $xp_next = $xp_prev; $xp_prev = $prev->xp; $energy = $prev->energy; $health = $prev->health; } } while ($xp >= $xp_next) { $next = sql("select * from z_levels where level = " . ($level + 1)); $mark = sql("select * from z_levels where level = " . ($level + 2)); if (!$mark || !$next) { $xp = $xp_next; break; } else { $level = $level+1; $xp_prev = $next->xp; $xp_next = $mark->xp; $energy = $next->energy; $health = $next->health; } } if ($level < $prevlev && $increment > 0) { error_log("Positive experience increment results in level loss: $prevxp -> $xp"); echo "00 ERROR: Positive experience increment results in level loss\n"; return; } if ($player->token == $token) { set_lust_progress('general', $xp, 'xp'); } $increment = $xp - $prevxp; error_log("{$player->token} {$player->name} gets $increment XP: $prevxp -> $xp"); sql("update z_players set xp = $xp, energy = $energy, energy_time = now(), health = $health, xp_prev = $xp_prev, xp_next = $xp_next where token = '{$player->token}'"); set_player_level($player, $level); clear_player_cache($player->token); // error_log("{$player->token} cache cleared"); if ($prevlev != $level) { $prev = $player; $player = update_player_stats($player->token); sql ("update z_players set health = health_max, energy = energy_max where token = '{$player->token}'"); error_log("{$player->token} {$player->name} levels up (level $prevlev -> $level)!\n". " - Energy: {$prev->energy_max} -> {$player->energy_max}\n". " - Health: {$prev->health_max} -> {$player->health_max}\n". " - Attack: {$prev->attack} -> {$player->attack}\n". " - Defense: {$prev->defense} -> {$player->defense}"); $player->energy = $player->energy_max; $player->health = $player->health_max; } return $xp - $prevxp; } function get_object_level($xp, $rarity) { $rarity = (int)$rarity; $xp = (int)$xp; return sql("select max(level) from z_object_levels where rarity <= $rarity and xp <= $xp"); } function get_object_maxxp($rarity) { $rarity = (int)$rarity; return sql("select max(xp) from z_object_levels where rarity = $rarity"); } function object_evolves_to ($object1, $object2) { if ($object1 == $object2) return true; $attempts = 10; while ($attempts > 0) { $object = get_object($object1); if (!$object) return false; if ($object->evolve_to == 0) return false; $destination = get_object($object->evolve_to); if (!$destination || $destination->upgrade_level == 0) return false; $object1 = $object->evolve_to; if ($object1 == $object2) return true; $attempts--; } return false; } function object_evolution_root ($object) { $alternative = sql("select object_code from z_alternate_evolution where alternative = $object"); if ($alternative) $object = $alternative; $base = sql("select code from z_objects where evolve_to = $object"); if ($base) return object_evolution_root($base); else return $object; } function add_medals($player, $medals, $reason = "") { global $job_bonus; $medals = (int)$medals; if ($medals == 0) return 0; if (is_string($player)) $player = get_player_profile($player); if ($medals > 0 && $player->guild) { $guild = get_guild_profile($player->guild); if ($guild) { $bonus = $job_bonus[$player->guild_job] * $guild->members; $prev = $medals; $medals = floor($medals + $bonus*$medals); error_log("{$player->token} {$player->name} Got a x$bonus medal bonus ($prev -> $medals)"); if ($guild->extra_medals) { require_once "version6/guild.php"; $guild_medals = ($medals * $guild->extra_medals / 100); add_guild_medals($guild->token, $guild_medals); error_log("{$player->token} {$player->name} Got $guild_medals medal(s) for the guild (from $medals)"); } } } add_currency($player->token, $medals, "medals", $reason); clear_player_cache($player->token); return $medals; } function add_coins($player, $coins, $guild = null) { global $job_bonus; $coins = (int)$coins; if (is_string($player)) $player = get_player_profile($player); if ($player->guild && $guild == null) $guild = get_guild_profile($player->guild); if ($guild) { $prev = $coins; $bonus = $job_bonus[$player->guild_job] * $guild->members; $coins = floor($coins + $bonus*$coins); if ($guild->extra_coins) { $bonus = $guild->extra_coins / 100; $coins = floor($coins + $bonus*$coins); } error_log("{$player->token} {$player->name} Got a x$bonus coins bonus ($prev -> $coins)"); } sql("update z_players set money = greatest(0, least(money_max, money + $coins)) where token = '{$player->token}'"); clear_player_cache($player->token); return $coins; } function get_player_price($player, $coins) { global $guild; $coins = (int)$coins; if (is_string($player)) $player = get_player_profile($player); if ($player->guild) { if ($guild == null) $guild = get_guild_profile($player->guild); if ($guild && $guild->discount) { $bonus = $guild->discount / 100; $prev = $coins; $coins = ceil($coins - $bonus*$coins); error_log("{$player->token} {$player->name} Got a x$bonus coins discount due to guild ($prev -> $coins)"); } } return $coins; } function make_shiny($object, $multiplier = 1) { global $shiny_chance; global $platinum_chance; global $token; $dice = rand(1, 100); $chance = $shiny_chance * $multiplier; if ($dice <= $chance) { if ($object->attack) $object->attack *= 2; if ($object->defense) $object->defense *= 2; $platinum_dice = rand(1, 100); if ($platinum_dice <= $platinum_chance) { if ($object->attack) $object->attack *= 2; if ($object->defense) $object->defense *= 2; error_log("$token Shiny platinum rolled ($dice < $chance / $platinum_dice < $platinum_chance): {$object->code} {$object->name} platinum shiny (ATK {$object->attack} DEF {$object->defense})"); return 2; } else { error_log("$token Shiny rolled ($dice < $chance / $platinum_dice >= $platinum_chance): {$object->code} {$object->name} shiny (ATK {$object->attack} DEF {$object->defense})"); return 1; } } else { sandbox_log("$token Shiny failed ($dice > $chance): {$object->code} {$object->name} normal (ATK {$object->attack} DEF {$object->defense})"); } return 0; } function get_guild_profile($token) { if (!$token) return ""; $guild = apcu_fetch("guild_$token"); if ($guild) return $guild; $guild = sql("select * from z_guilds where token = '$token'"); $count = sql("select count(*) from z_players where guild = '$token'"); if ($guild->members != $count) { $guild->members = $count; sql("update z_guilds set members = $count where token = '$token'"); } apcu_store("guild_$token", $guild); return $guild; } function get_max_lust_points($girl) { $max_level = sql("select images from z_lust_girls where id = '$girl'")+1; if (!$max_level) $max_level = 7; $max_points = (int)sql("select min(points) from z_lust_levels where level > $max_level") - 1; if ($max_points < 0) $max_points = (int)sql("select max(points) from z_lust_levels"); // error_log("* Max points for $girl: $max_points at level $max_level"); return $max_points; } function add_lust_progress($girl, $progress, $coin = null) { global $mysqli, $token; if ($coin) { $lust = (int)get_game_variable("{$coin}_to_lust"); if ($lust > 0) { $progress = $progress / $lust; } else { error_log("PHP Error: $token tried to add lust points as {$coin} but there is no conversion rate"); return; } } $progress = (int)$progress; if ($progress <= 0) { return; } $girl = $mysqli->real_escape_string($girl); $max_points = get_max_lust_points($girl); sql ("insert into z_lust_progress (player_token, girl, points, level) values('$token', '$girl', least($max_points, $progress), 1) on duplicate key update points = least($max_points, points + $progress)"); sql ("update z_lust_progress set level = ifnull((select max(level) from z_lust_levels where z_lust_levels.points <= z_lust_progress.points), 1) where player_token = '$token' and girl = '$girl'"); $row = sql("select points, level from z_lust_progress where player_token = '$token' and girl = '$girl'"); error_log("Player $token added $progress lust points to $girl (current: {$row->points}, level {$row->level})"); } function increase_lust_level($girl) { global $token, $mysqli; $girl = $mysqli->real_escape_string($girl); $points = (int)sql("select points from z_lust_progress where player_token = '$token' and girl = '$girl'"); $level = sql("select min(level) from z_lust_levels where z_lust_levels.points >= $points"); if (!$level) return; $required = sql("select min(points) from z_lust_levels where level > $level"); if ($required) { error_log("Player $token increased lust level for $girl ($required points required at level $level)"); set_lust_progress($girl, $required); } else { error_log("Player $token tried to increase lust level for $girl but it's already at maximum ($points, $required)"); } } function set_lust_progress($girl, $points, $coin = null) { global $mysqli, $token; if ($coin) { $lust = (int)sql("select value from z_game_variables where variable = '{$coin}_to_lust'"); if ($lust > 0) { $points = $points / $lust; } else { error_log("PHP Error: $token tried to set lust points as {$coin} but there is no conversion rate"); return; } } $points = (int)$points; $max_points = get_max_lust_points($girl); if ($points > $max_points) $points = $max_points; $girl = $mysqli->real_escape_string($girl); $level = sql("select max(level) from z_lust_levels where z_lust_levels.points <= $points"); if (!$level) $level = 1; sql ("insert into z_lust_progress (player_token, girl, points, level) values('$token', '$girl', $points, $level) on duplicate key update points = greatest(points, $points), level = ifnull((select max(level) from z_lust_levels where z_lust_levels.points <= greatest($points, z_lust_progress.points)), level)"); error_log("Player $token set $points lust points to $girl"); } function has_subscription($girl, $player_token = null) { global $token; if ($player_token == null) $player_token = $token; $existing = sql("select count(*) from z_xxx_subscriptions where player_token = '$player_token' and expires > now()"); return $existing > 0; } function complete_task($code) { global $token; $player = get_player_profile($token); $code = (int)$code; $task = sql("select * from z_tasks where code = $code"); if (!$task) { error_log("PHP Error: attempt to complete non-existing task $code"); return 0; } if ($task->min_level && $player->level < $task->min_level) { // error_log("Player $token failed to complete task $code, level $player->level < $task->min_level"); return 0; } $requires = (int)$task->requires; if ($requires) { $required = sql("select count(*) from z_tasks_done where player_token = '$token' and task_code in ($requires)"); if ($required == 0) { // error_log("Player $token failed to complete task $code, requirements $requires not meet"); return 0; } } if ($task->one_time_only) { $done = sql("select count(*) from z_tasks_done where player_token = '$token' and task_code = $code"); if ($done) { // error_log("Player $token failed to complete task $code, already done"); return 0; } } $name = get_player_name($token); error_log("$token $name completed task $code"); sql("insert into z_tasks_done (player_token, task_code) values ('$token', $code)"); sql("update z_players set pending_tasks = pending_tasks + 1 where token = '$token'"); return 1; } // -------------------------------------------------------------------- // Mercs // -------------------------------------------------------------------- function update_mercs_stats($token, $merc = null) { $profile = get_player_profile($token); clear_mercs_cache($token); if ($merc == null) { error_log("update_mercs_stats: updating all mercs for $token"); $query = sql_multiple("select * from z_mercs_inventory where player_token = '$token' and equipped_slot > 0"); while (($merc = $query->fetch_object()) != null) update_mercs_stats($token, $merc); return; } if (is_numeric($merc)) { $slot = (int)$merc; $merc = sql("select * from z_mercs_inventory where player_token = '$token' and equipped_slot = $slot"); if (!$merc) { error_log("update_mercs_stats: MERC $slot NOT FOUND"); return; } } else { $slot = $merc->equipped_slot; if (!$slot) { $girl = get_lust_girl($merc->girl); $merc->current_attack = $merc->attack; $merc->current_defense = $merc->defense; $merc->current_energy = $merc->energy; $merc->weapons = (int)$girl->weapons; sql("update z_mercs_inventory set current_attack = $merc->attack, current_defense = $merc->defense, current_energy = $merc->energy, weapons = $merc->weapons where player_token = '$token' and sort_order = $merc->sort_order"); $player_name = get_player_name($token); sandbox_log("---------------------- $player_name #$merc->sort_order $merc->girl $girl->name stats (unequipped)"); return; } } $player = get_player_profile($token); $player_name = $player->name; $girl = get_lust_girl($merc->girl); if (!$girl) { error_log("update_mercs_stats: GIRL $merc->girl NOT FOUND"); return; } sandbox_log("---------------------- $player_name updating #$merc->sort_order $merc->girl \033[32m$girl->name\033[0m stats"); $max_weapons = (int)$girl->weapons; if (!$max_weapons) $max_weapons = 2; $tags = []; if ($girl->tag) $tags[] = $girl->tag; if ($merc->skin) $tags[] = get_skin_tag($merc->skin); $pvp = get_merc_pvp_skills($token, $merc); $merc->current_attack = 0; $merc->current_defense = $pvp->health; $merc->current_energy = $merc->energy; $merc->current_boost = 0; $merc->current_until = null; if ($profile->rage_seconds > 0) { $merc->current_until = date("Y-m-d H:i:s", time() + $profile->rage_seconds); } $merc_skills = [ sql("select * from z_merc_skills where id = $girl->attack_skill") ]; $skin_skill = null; if ($merc->skin) { $skin_skill = sql("select skill from z_skins where id = $merc->skin"); if ($skin_skill) { $merc_skills[] = sql("select * from z_merc_skills where id = $skin_skill"); } } if ($girl->tag) { $tag_skill = sql("select skill from z_skins where tag = '{$girl->tag}'"); if ($tag_skill) { if ($tag_skill != $girl->attack_skill && $tag_skill != $skin_skill) { $merc_skills[] = sql("select * from z_merc_skills where id = $tag_skill"); } } } // x10 multiplier for all skills when the mercenary is at evolution 4+ if ($merc->evolution == 4) { foreach ($merc_skills as $m_skill) { if ($m_skill->multiplier_evo4 != null) $m_skill->multiplier = $m_skill->multiplier_evo4; } } $merc_skill_specials = []; foreach ($merc_skills as $merc_skill) { if ($merc_skill->special) { $merc_skill_specials[] = $merc_skill->special; } if ($merc_skill->max_weapons !== null && $max_weapons > $merc_skill->max_weapons) { $max_weapons = (int)$merc_skill->max_weapons; } } $a = $slot*10; $b = $slot*10 + 9; $weapon_count = 0; $total_multiplier = 0; $def_as_atk = false; // Set weapons_missing if the merc has a skill that requires all weapons and the player doesn't have all of them equipped $weapons_missing = 0; foreach ($merc_skills as $merc_skill) { if (str_starts_with($merc_skill->special, "def-as-atk")) { $def_as_atk = true; } if ($merc_skill->special == "all-weapons") { $weapon_from = $merc_skill->weapon_from; $weapon_to = $merc_skill->weapon_to; $weapons_mask = 0; $matches = 0; $first_weapons = []; $first_weapons_query = sql_multiple("select code from z_objects where code between $weapon_from and $weapon_to and upgrade_level = 0 order by code"); while (($row = $first_weapons_query->fetch_object()) != null) { $first_weapons[] = $row->code; } $query = sql_multiple("select object_code from z_inventory where player_token = '$token' and equipped_slot between $a and $b"); while (($row = $query->fetch_object()) != null) { $object_index = 0; for ($n = 1; $n < count($first_weapons); $n++) { if ($row->object_code >= $first_weapons[$n]) { $object_index++; } } sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: weapon {$row->object_code} has all-weapons index $object_index (first weapons: " . join(",", $first_weapons) . ")"); if ($row->object_code >= $weapon_from && $row->object_code <= $weapon_to) { $weapons_mask |= 1 << $object_index; $matches++; } } $bit_count = count($first_weapons); if ($weapons_mask != (1 << $bit_count) - 1) { $weapons_missing = 1; } if ($matches != $max_weapons) { $weapons_missing = 1; } } } $skill_attack = 0; $weapons = []; $query = sql_multiple("select *, time_to_sec(timediff(boosted_until, now())) as boosted from z_inventory where player_token = '$token' and equipped_slot between $a and $b"); while (($object = $query->fetch_object()) != null) { $weapons[] = $object; if (!$object->skill) $object->skill = 1000; if (!$weapons_missing) { $multiplier = get_merc_skill_multiplier($merc_skills, $object); } else { $multiplier = 1; } $weapon_skills = [ get_skill($object->skill) ]; if ($object->sb_unlocked) { $weapon_info = get_object($object->object_code); if ($weapon_info->sb_mode != "defense") { $merc_links = array_map('intval', explode(",", $weapon_info->merc_links)); if (in_array((int)$girl->code, $merc_links)) { $weapon_skills[] = get_skill($weapon_info->skill_sb); sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: weapon {$object->object_code} has SB unlocked"); } else { sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: weapon {$object->object_code} has SB unlocked but not for this merc $girl->code"); } } } $skill_penetration = 0; $skill_magnitude = 1; foreach ($weapon_skills as $skill) { if ($skill->penetration) { $skill_penetration = 1; } $skill_magnitude *= $skill->magnitude; sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: weapon {$object->object_code} has skill {$skill->code} {$skill->action} (magnitude is now $skill_magnitude, penetration $skill->penetration, attack $skill->attack)"); $skill_attack += $skill->attack; if ($skill->action == "defense down") { sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: weapon {$object->object_code} has skill {$skill->code} {$skill->name} (defense down), setting magnitude to 1"); $skill_magnitude = 1; } } if ($skill_penetration && in_array("cyborg", $tags)) { $multiplier *= 2; sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: weapon {$object->object_code} has penetration and cyborg tag, doubling multiplier to x$multiplier"); } if ($object->boosted && $object->boost > 1) { $multiplier *= $object->boost; $merc->current_boost = 1; if ($merc->current_until == null || strtotime($merc->current_until) < strtotime($object->boosted_until)) { $merc->current_until = $object->boosted_until; } sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: weapon {$object->object_code} has boost x$object->boost, multiplying multiplier to x$multiplier"); } if ($def_as_atk) $weapon_attack = $object->defense * $multiplier; else $weapon_attack = $object->attack * $multiplier; $total_multiplier += $multiplier; $previous_weapon_attack = $weapon_attack; $weapon_attack = $skill_attack + $skill_magnitude * $weapon_attack; sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: weapon {$object->object_code} has $weapon_attack ATK (multiplier x$multiplier, skill adds $skill_attack + ATK$previous_weapon_attack x $skill_magnitude)"); $boost = 1; if ($object->boost) $boost = max(1, $object->boost); $merc->current_attack += $weapon_attack * $boost; if ($object->skill_energy == 0) { $weapon_object = get_object($object->object_code); $skill = get_skill($object->skill || 1000); $base_energy = $skill->energy_cost * (1 << $weapon_object->rarity); $new_energy = $base_energy * pow(0.95, $object->skill_level-1); $object->skill_energy = $new_energy; // $player_name = get_player_name($token); sandbox_log("$token $player_name, weapon {$object->object_code} has no energy cost, setting to $new_energy"); sql("update z_inventory set skill_energy = $new_energy where player_token = '$token' and sort_order = {$object->sort_order}"); clear_inventory_cache($token); } $merc->current_energy += $object->skill_energy; $weapon_count++; } sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: total multiplier x$total_multiplier for $weapon_count weapons"); if ($def_as_atk) { if ($total_multiplier > 0) $merc->current_attack += $merc->defense * $total_multiplier/$weapon_count; else $merc->current_attack += $merc->defense; } else { if ($total_multiplier > 0) $merc->current_attack += $merc->attack * $total_multiplier/$weapon_count; else $merc->current_attack += $merc->attack; } if ($skill_attack) { $merc->current_attack += $skill_attack; sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: ATK $merc->current_attack DEF $merc->current_defense ENE $merc->current_energy (incremented by $skill_attack)"); } $def_as_atk_multiplier = 1; foreach ($merc_skills as $merc_skill) { $requirements_meet = true; if ($merc_skill->weapon_list) { $weapon_list = explode(",", $merc_skill->weapon_list); $requirements_meet = false; foreach ($weapons as $weapon) { if (in_array($weapon->object_code, $weapon_list)) { $requirements_meet = true; break; } } } if ($merc_skill->weapon_from && $merc_skill->weapon_to) { $requirements_meet = false; foreach ($weapons as $weapon) { if ($weapon->object_code >= $merc_skill->weapon_from && $weapon->object_code <= $merc_skill->weapon_to) { $requirements_meet = true; break; } } } if (!$requirements_meet) { sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: merc skill $merc_skill->id $merc_skill->name ($merc_skill->special) requirements not meet"); } if ($merc_skill->special) switch ($merc_skill->special) { case "def-as-atk": if ($requirements_meet) { $def_as_atk_multiplier = $merc_skill->multiplier; } break; case "all-weapons": // Multiplier already applied to weapons if all weapons are equipped break; case "evo-increment": if ($requirements_meet) { $inc = (int)pow(10, $merc_skill->parameter * ($merc->evolution + 1)) - 1; $merc->current_attack += $inc; sandbox_log("evo-update_mercs_stats: adding $inc to ATK due to evo-increment ($merc->evolution evolutions, $merc_skill->parameter parameter)"); } break; case "unarmed": if ($weapon_count == 0) { sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: unarmed skill $merc_skill->id multiplier x$merc_skill->multiplier"); $merc->current_attack *= $merc_skill->multiplier; } break; case "lucky strike": if ($merc_skill->parameter) { $value = floor($merc_skill->parameter/2); } else { $value = 5; } $merc->current_attack *= $value; sandbox_log("update_mercs_stats: lucky strike applies x$value multiplier"); break; /* Add a multiplier that depends on the kind of weapon equipped */ case "shamrock": if ($requirements_meet) { $value = 1; foreach ($weapons as $weapon) { if ($weapon->object_code >= 844 && $weapon->object_code <= 849) { $value = max($value, 10); } elseif ($weapon->object_code >= 850 && $weapon->object_code <= 855) { $value = max($value, 100); } elseif ($weapon->object_code >= 856 && $weapon->object_code <= 861) { $value = max($value, 1000); } elseif ($weapon->object_code >= 862 && $weapon->object_code <= 867) { $value = max($value, 10000); } } if ($merc_skill->multiplier) { $value *= $merc_skill->multiplier; } $merc->current_attack *= $value; sandbox_log("update_mercs_stats: shamrock applies x$value multiplier"); } break; case "alone-multiplier": if (!$requirements_meet) break; $on_squad = sql("select count(*) from z_mercs_inventory where player_token = '$token' and equipped_slot is not null"); if ($on_squad == 1) { $new_attack = $merc->current_attack * $merc_skill->multiplier; sandbox_log("update_mercs_stats: $token $player_name #$merc->sort_order $girl->name: alone multiplier x$merc_skill->multiplier applies ($merc->current_attack -> $new_attack)"); $merc->current_attack = $new_attack; } else { error_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: not alone on squad, x$merc_skill->multiplier multiplier not applied"); } break; case "on-squad": if (!$requirements_meet) break; $list = $merc_skill->parameter; if ($list) { $girls = sql_join("select id from z_lust_girls where code in ($list)"); $on_squad = sql("select count(*) from z_mercs_inventory where player_token = '$token' and equipped_slot is not null and girl in ($girls)"); if ($on_squad) { $new_attack = $merc->current_attack * $merc_skill->multiplier; sandbox_log("update_mercs_stats: $token $player_name #$merc->sort_order $girl->name: on-squad multiplier x$merc_skill->multiplier for $list $girls $on_squad applies"); sandbox_log("update_mercs_stats: $token $player_name #$merc->sort_order $girl->name on-squad $merc->current_attack x $merc_skill->multiplier = $new_attack"); $merc->current_attack = $new_attack; } else { error_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: not on-squad"); } } break; case "recent-nurse-refill-multiplier": if (!$requirements_meet) break; $hours = (int)$merc_skill->parameter; $recent = sql("select last_nurse_refill is not null and last_nurse_refill > date_sub(now(), interval $hours hour) from z_players where token = '$token'"); if ($recent) { $merc->current_attack *= $merc_skill->multiplier; sandbox_log("update_mercs_stats: recent-nurse-refill-multiplier: x$merc_skill->multiplier applies (last nurse refill within last $hours hours)"); } else { sandbox_log("update_mercs_stats: recent-nurse-refill-multiplier: no multiplier applied (last nurse refill not within last $hours hours)"); } break; case "recent-nutaku-purchase-multiplier": if (!$requirements_meet) break; $hours = (int)$merc_skill->parameter; $recent = sql("select count(*) from z_purchases where player_token = '$token' and date > date_sub(now(), interval $hours hour)"); if ($recent) { $merc->current_attack *= $merc_skill->multiplier; sandbox_log("update_mercs_stats: recent-nutaku-purchase-multiplier: x$merc_skill->multiplier applies (recent purchases within last $hours hours)"); } else { sandbox_log("update_mercs_stats: recent-nutaku-purchase-multiplier: no multiplier applied (no purchases within last $hours hours)"); } break; case "recent-parts-box-multiplier": if (!$requirements_meet) break; $hours = (int)$merc_skill->parameter; $recent = sql("select last_parts_box is not null and last_parts_box > date_sub(now(), interval $hours hour) from z_players where token = '$token'"); if ($recent) { $merc->current_attack *= $merc_skill->multiplier; sandbox_log("update_mercs_stats: recent-parts-box-multiplier: x$merc_skill->multiplier applies (last parts box within last $hours hours)"); } else { sandbox_log("update_mercs_stats: recent-parts-box-multiplier: no multiplier applied (last parts box not within last $hours hours)"); } break; case "recent-gens-box-multiplier": if (!$requirements_meet) break; $hours = (int)$merc_skill->parameter; $recent = sql("select last_gens_box is not null and last_gens_box > date_sub(now(), interval $hours hour) from z_players where token = '$token'"); if ($recent) { $merc->current_attack *= $merc_skill->multiplier; sandbox_log("update_mercs_stats: recent-gens-box-multiplier: x$merc_skill->multiplier applies (last gens box within last $hours hours)"); } else { sandbox_log("update_mercs_stats: recent-gens-box-multiplier: no multiplier applied (last gens box not within last $hours hours)"); } break; case "log-in-multiplier": if ($requirements_meet) { $login_multiplier = $merc_skill->multiplier * min($merc_skill->parameter, $player->streak); if ($login_multiplier < 1) $login_multiplier = 1; $merc->current_attack *= $login_multiplier; sandbox_log("update_mercs_stats: x$login_multiplier multiplier for $player->streak consecutive logins"); } break; case "unarmed-rage": if (count($weapons) != 0) $requirements_meet = false; case "rage": if ($profile->rage_seconds > 0 && $requirements_meet) $merc->current_attack *= $merc_skill->multiplier; break; case "vip-multiplier": if (!$requirements_meet) break; $vip_level = (int)$player->vip; if ($vip_level) { $multiplier = $merc_skill->multiplier; if ($vip_level >= 2 && $merc_skill->parameter) { $multiplier *= (int)$merc_skill->parameter; } $merc->current_attack *= $multiplier; sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: vip-multiplier x$multiplier applies (vip level $vip_level)"); } else { sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: vip-multiplier not applied (no vip level)"); } break; case "payoff500": if (!$requirements_meet) break; $payoff500 = get_game_variable("payoff500"); if (!$payoff500) $payoff500 = 100; $jewels = (int)sql("select sum(jewels) from z_purchases where player_token = '$token'"); $multiplier = (1 + $jewels / $payoff500); if ($merc_skill->multiplier) $multiplier *= $merc_skill->multiplier; sandbox_log("update_mercs_stats: $player_name payoff500: $jewels jewels purchased, x$multiplier"); $merc->current_attack *= $multiplier; break; case "def-as-atk-payoff-halloween": $merc_skill->special = substr($merc_skill->special, 11); case "payoff-halloween": case "payoff-black": case "payoff-jingle": case "payoff-mimic": case "payoff-tomoe": case "payoff-zombies": if (!$requirements_meet) break; $payoff = get_game_variable($merc_skill->special); if (!$payoff) $payoff = 50; $field = get_payoff_spent_field($merc_skill->special); $spent = (int)sql("select $field from z_players where token = '$token'"); $multiplier = (1 + $spent / $payoff); if ($merc_skill->multiplier) $multiplier *= $merc_skill->multiplier; error_log("update_mercs_stats: $player_name $girl->name EVO$merc->evolution $merc_skill->special: $spent spent, x$multiplier: (1 + $spent / $payoff) * $merc_skill->multiplier def_as_atk=$def_as_atk"); if ($def_as_atk) { $merc->current_defense = round($merc->current_defense * $multiplier); } else { $merc->current_attack = round($merc->current_attack * $multiplier); } break; case "money-as-energy-between-hours": case "health-as-energy-between-hours": if ($requirements_meet) { // Check that the current hour fits the skill's parameter (i.e. 6-8 for 6-8 AM) $timezone = sql("select timezone from z_mission_areas where hidden = 0 and released = 1 and timezone is not null"); if (!$timezone) { $timezone = "UTC"; } $dt = new DateTime("now", new DateTimeZone($timezone)); $current_hour = (int)$dt->format("H"); $hours = explode("-", $merc_skill->parameter); $requirements_meet = false; if (count($hours) == 1) { $requirements_meet = ($current_hour == (int)$hours[0]); } else if (count($hours) == 2) { $hour_from = (int)$hours[0]; $hour_to = (int)$hours[1]; if ($hour_from > $hour_to) { $requirements_meet = ($current_hour >= $hour_from || $current_hour <= $hour_to); } else { $requirements_meet = ($current_hour >= $hour_from && $current_hour <= $hour_to); } } if ($requirements_meet) { $merc->current_attack *= $merc_skill->multiplier; error_log("$token {$player->name} health-as-energy-between-hours skill check: $timezone current hour $current_hour for range {$merc_skill->parameter}, requirements meet"); } else { error_log("$token {$player->name} health-as-energy-between-hours skill check: $timezone current hour $current_hour for range {$merc_skill->parameter}, requirements not meet"); } } break; default: sandbox_log("update_mercs_stats: Ignoring mercenary skill special {$merc_skill->special}"); break; } } if ($def_as_atk) { sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: DEF as ATK multiplier x$def_as_atk_multiplier with def $merc->current_defense"); $merc->current_attack = $merc->current_defense * $def_as_atk_multiplier; } if ($profile->rage_seconds > 0 && $profile->rage_multiplier > 1) { $merc->current_attack *= $profile->rage_multiplier; $merc->current_boost = $profile->rage_multiplier; } sandbox_log("update_mercs_stats: $player_name #$merc->sort_order $girl->name: ATK $merc->current_attack DEF $merc->current_defense ENE $merc->current_energy"); $current_until = $merc->current_until == null ? "null" : "'$merc->current_until'"; sql("update z_mercs_inventory set current_attack = $merc->current_attack, current_defense = $merc->current_defense, current_energy = $merc->current_energy, current_boost = $merc->current_boost, current_until = $current_until, weapons = $max_weapons where player_token = '$token' and equipped_slot = $slot"); $merc->weapons = $max_weapons; } function get_merc_skill_multiplier($skill_list, $weapon) { if (!$skill_list) return 0; if (is_object($skill_list)) $skill_list = [ $skill_list ]; elseif (is_numeric($skill_list)) $skill_list = [ sql("select * from z_merc_skills where id = $skill_list") ]; $total_multiplier = 1; foreach ($skill_list as $skill) { $multiplier = $skill->multiplier; // A 0 multiplier resets the multiplier to 1 if (!$multiplier) { $multiplier = 1; } // Reset the multiplier if the weapon is not in the skill list if ($skill->weapon_list) { $weapon_list = explode(",", $skill->weapon_list); if (!in_array($weapon->object_code, $weapon_list)) { sandbox_log("get_merc_skill_multiplier: $skill->name, weapon $weapon->object_code not in list, multiplier reset to 1"); $multiplier = 1; } } // Reset the multiplier if the weapon is outside the skill range or has no required skill/feature if ($skill->weapon_from && $skill->weapon_to && ($weapon->object_code < $skill->weapon_from || $weapon->object_code > $skill->weapon_to)) { sandbox_log("get_merc_skill_multiplier: $skill->name, weapon $weapon->object_code outside range, multiplier reset to 1"); $multiplier = 1; } if ($skill->required_skill) { $weapon_skill = (int)$weapon->skill; $action = sql("select action from z_skills where code = $weapon_skill"); if ($action != $skill->required_skill) { sandbox_log("get_merc_skill_multiplier: $skill->name, weapon $weapon->object_code has no required skill, multiplier reset to 1"); $multiplier = 1; } } if ($skill->special == "penetration") { $weapon_skill = (int)$weapon->skill; $penetration = sql("select penetration from z_skills where code = $weapon_skill"); if (!$penetration) { sandbox_log("get_merc_skill_multiplier: $skill->name, weapon $weapon->object_code has no penetration, multiplier reset to 1"); $multiplier = 1; } } if ($skill->special == "uncommon") { $rarity = sql("select rarity from z_objects where code = {$weapon->object_code}"); if ($rarity > 1) { sandbox_log("get_merc_skill_multiplier: $skill->name, weapon $weapon->object_code is not uncommon, multiplier reset to 1"); $multiplier = 1; } } if ($skill->special == "rage" || $skill->special == "unarmed" || $skill->special == "on-squad" || $skill->special == "first-attack" || $skill->special == "boss-skill-multiplier" || $skill->special == "defense" || $skill->special == "log-in-multiplier" || $skill->special == "commando" || str_starts_with($skill->special, "def-as-atk-payoff") || str_starts_with($skill->special, "payoff") || str_starts_with($skill->special, "energy") || str_starts_with($skill->special, "recent")) { // Those skills do not apply the multiplier to weapons damage sandbox_log("get_merc_skill_multiplier: $skill->name, weapon $weapon->object_code special {$skill->special}, multiplier reset to 1"); $multiplier = 1; } sandbox_log("get_merc_skill_multiplier: $skill->name, weapon $weapon->object_code: x$multiplier"); $total_multiplier *= $multiplier; } sandbox_log("get_merc_skill_multiplier: Skill $skill->name, weapon $weapon->object_code: x$multiplier"); return $total_multiplier; } function get_player_server($token) { $server = null; $cached = apcu_fetch("player_$token"); if ($cached !== false) $server = is_numeric(@$cached->server) ? $cached->server : null; if ($server == null) $server = (int)sql("select server from z_players where token = '$token'"); return $server; } class MercPVPSkills { var $health; var $defenseMultiplier; var $defenseIncrement; var $skillsList; var $skills; var $weapons; } function get_merc_pvp_skills($token, $merc, $tutorial = false) { $name = get_player_name($token); $result = new MercPVPSkills(); $result->defenseMultiplier = 1; // Account for two magnitudes $result->defenseIncrement = 0; $result->skills = []; $result->weapons = []; $result->health = 0; // ---- Validate input ---- if (is_numeric($merc)) { $slot = (int)$merc; $merc = sql("select * from z_mercs_inventory where player_token = '$token' and equipped_slot = $slot"); if (!$merc) { error_log("$token $name Attempt to get merc $slot skills, no merc equipped in that slot"); return $result; } } $slot = $merc->equipped_slot; if (!$slot) { error_log("$token $name Attempt to get merc #$merc->sort_order $merc->girl skills, but she is not equipped"); return $result; } $girl = get_lust_girl($merc->girl); if (!$girl) { error_log("$token $name Attempt to get merc $slot skills, girl $merc->girl not found"); return $result; } $result->health = $merc->defense; if (!$result->health) { error_log("$token $name Merc $slot $merc->girl has no defense"); } else { sandbox_pvp_log("[PVP] $token $name Calculating health for #$merc->sort_order $merc->girl with $result->health defense"); } // ----- $tags = []; if ($girl->tag) $tags[] = $girl->tag; if ($merc->skin) $tags[] = get_skin_tag($merc->skin); $actions = []; $equipped_weapon_actions = []; $suit_skills = [ $girl->defense_skill, $girl->defense_skill_2 ]; // ----- Check equipped weapons for defense and soulbound skills ----- $slot_1 = $merc->equipped_slot * 10; $slot_x = $slot_1 + 9; $query = sql_multiple("select *, time_to_sec(timediff(boosted_until, now())) as boosted from z_inventory where player_token = '$token' and equipped_slot between $slot_1 and $slot_x"); while (($row = $query->fetch_object()) != null) { $weapon_info = get_object($row->object_code); $result->weapons[] = $row->object_code; $weapon_skill = get_skill($row->skill); $equipped_weapon_actions[] = $weapon_skill->action; $defense = $row->defense; sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: weapon {$row->object_code} has defense $defense, skill {$weapon_skill->code} {$weapon_skill->name} (action {$weapon_skill->action})"); // Check if the weapon has a defense soulbound skill if ($row->sb_unlocked) { if ($weapon_info->sb_mode == "defense") { $merc_links = array_map('intval', explode(",", $weapon_info->merc_links)); if (in_array((int)$girl->code, $merc_links)) { $sb_skill = get_skill($weapon_info->skill_sb); if ($sb_skill) { if ($sb_skill->magnitude) $defense *= $sb_skill->magnitude; if ($sb_skill->defense) $defense += $sb_skill->defense; $skill_actions = array_map(function($action_name) use ($sb_skill) { return [ "action" => $action_name, "info" => $sb_skill ]; }, explode(",", $sb_skill->action)); $actions = array_merge($actions, $skill_actions); sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: weapon {$row->object_code} has SB unlocked, defense x $sb_skill->magnitude, increment $sb_skill->defense, adding actions $sb_skill->action"); } } else { sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: weapon {$row->object_code} has SB unlocked but not for this merc $girl->code"); } } } // Apply weapon boost if ($row->boosted && $row->boost) { $defense *= $row->boost; } sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: weapon {$row->object_code} adds defense $defense"); $result->health += $defense; } // ----- Apply defense/add-skill PVP effects from merc skills ----- $skin_skill = null; $merc_skills = [ $girl->attack_skill ]; if ($merc->skin) { $skin_skill = sql("select skill from z_skins where id = $merc->skin"); if ($skin_skill) { $merc_skills[] = $skin_skill; } } if ($girl->tag) { $tag_skill = sql("select skill from z_skins where tag = '{$girl->tag}'"); if ($tag_skill) { if ($tag_skill != $girl->attack_skill && $tag_skill != $skin_skill) { $merc_skills[] = $tag_skill; } } } foreach ($merc_skills as $skill) { $info = sql("select * from z_merc_skills where id = $skill"); if (!$info) { error_log("$token $name merc $slot has merc skill $skill which was not found"); continue; } if ($info->special != "defense" && $info->special != "add-skill" && !str_starts_with($info->special, "payoff-defense")) { continue; } if ($merc->evolution == 4 && $info->multiplier_evo4) { $info->multiplier = $info->multiplier_evo4; sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: merc skill $skill will use multiplier x$info->multiplier (for evolution 4)"); } // Check if any of the equipped weapons has the required skill if ($info->required_skill) { if (!in_array($info->required_skill, $equipped_weapon_actions)) { sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: merc skill $skill has no effect on PVP, required skill $info->required_skill not found"); continue; } } // Check if any of the weapons in $result->weapons is in the range if ($info->weapon_from && $info->weapon_to) { $found = false; foreach ($result->weapons as $weapon) { if ($weapon >= $info->weapon_from && $weapon <= $info->weapon_to) { $found = true; break; } } if (!$found) { sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: merc skill $skill has no effect on PVP, weapon not found in range $info->weapon_from - $info->weapon_to"); continue; } } if ($info->special == "defense") { sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: defense skill $skill adds defense multiplier x$info->multiplier"); $result->defenseMultiplier += $info->multiplier - 1; } else if ($info->special == "add-skill" && $info->parameter) { sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: merc skill $skill adds skill $info->parameter"); $suit_skills[] = $info->parameter; } else if (str_starts_with($info->special, "payoff-defense")) { $payoff = get_game_variable($info->special); if (!$payoff) $payoff = 50; $field = get_payoff_spent_field($info->special); $spent = (int)sql("select $field from z_players where token = '$token'"); $multiplier = (1 + $spent / $payoff); if ($info->multiplier) $multiplier *= $info->multiplier; sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: $info->special: $spent spent, x$multiplier added to defense"); $result->defenseMultiplier += $multiplier; } } // ----- Get defense multiplier and increment from suit skills ----- foreach ($suit_skills as $skill) { $info = sql("select action, magnitude, chance, energy_cost, defense from z_skills where code = $skill"); if (!$info) { error_log("$token $name merc $slot $merc->girl skill $skill not found"); continue; } $info_actions = explode(",", $info->action); foreach ($info_actions as $action) $actions[] = [ "action" => $action, "info" => $info ]; if (!$info->magnitude) $info->magnitude = 1; sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: skill $skill adds defense multiplier x$info->magnitude, increment $info->defense"); $result->defenseMultiplier += $info->magnitude - 1; $result->defenseIncrement += $info->defense; } // ----- Filter resulting actions from equipped weapons and merc skills ----- foreach ($actions as $item) { $action = $item["action"]; $info = $item["info"]; $value = -1; switch ($action) { case "resistances": $material = $girl->material; switch ($material) { case 1: $action = "resists blade"; $result->skills["weak against mechanical"] = -1; break; case 2: $action = "resists blunt"; $result->skills["weak against blade"] = -1; break; case 3: $action = "resists mechanical"; $result->skills["weak against blunt"] = -1; break; } break; case "immunities": $material = $girl->material; switch ($material) { case 1: $action = "immune against blade"; $result->skills["weak against mechanical"] = -1; break; case 2: $action = "immune against blunt"; $result->skills["weak against blade"] = -1; break; case 3: $action = "immune against mechanical"; $result->skills["weak against blunt"] = -1; break; } break; case "heals": $material = $girl->material; switch ($material) { case 1: $action = "heals from blade"; $result->skills["weak against mechanical"] = -1; break; case 2: $action = "heals from blunt"; $result->skills["weak against blade"] = -1; break; case 3: $action = "heals from mechanical"; $result->skills["weak against blunt"] = -1; break; } break; default: if ($info->chance) { $value = $info->chance; if (isset($result->skills[$action])) { sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: skill $skill has conflicting actions $action, adding $value to the existing value"); $value += $result->skills[$action]; } else { sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: skill $skill adds action $action with chance value $value"); } } elseif ($info->energy_cost) { $value = $info->energy_cost; if (isset($result->skills[$action])) { sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: skill $skill has conflicting actions $action, adding $value to the existing value"); $value += $result->skills[$action] - 1; } else { sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: skill $skill adds action $action with energy cost $value"); } } break; } // TODO: Accumulate or leave only the greatest value? if (!isset($result->skills[$action]) || $result->skills[$action] < $value) { sandbox_pvp_log("[PVP] $token $name #$merc->sort_order $girl->name: skill $skill adds action $action with value $value"); $result->skills[$action] = $value; } } // Hack: slime tag gives resistance to all weapons if (in_array("slime", $tags) || in_array("merlime", $tags) || in_array("maidrylime", $tags) || in_array("maidborg", $tags)) { $result->skills["resists blade"] = 1; $result->skills["resists blunt"] = 1; $result->skills["resists mechanical"] = 1; if (isset($result->skills["weak against blade"])) unset($result->skills["weak against blade"]); if (isset($result->skills["weak against blunt"])) unset($result->skills["weak against blunt"]); if (isset($result->skills["weak against mechanical"])) unset($result->skills["weak against mechanical"]); } // Handle conflicting skills foreach ([ "blade", "blunt", "mechanical" ] as $material) { // Leave only the greatest resistance if (isset($result->skills["resists $material"]) && isset($result->skills["immune against $material"])) { unset($result->skills["resists $material"]); } if (isset($result->skills["resists $material"]) && isset($result->skills["heals from $material"])) { unset($result->skills["resists $material"]); } if (isset($result->skills["heals from $material"]) && isset($result->skills["immune against $material"])) { unset($result->skills["immune against $material"]); } // If weak and resists, remove weak and decrease resistance level if (isset($result->skills["heals from $material"]) && isset($result->skills["weak against $material"])) { unset($result->skills["weak against $material"]); unset($result->skills["heals from $material"]); $result->skills["immune against $material"] = 1; } if (isset($result->skills["immune against $material"]) && isset($result->skills["weak against $material"])) { unset($result->skills["weak against $material"]); unset($result->skills["immune against $material"]); $result->skills["resists $material"] = 1; } if (isset($result->skills["resists $material"]) && isset($result->skills["weak against $material"])) { unset($result->skills["weak against $material"]); unset($result->skills["resists $material"]); } } // Hack: in tutorial, remove all skills except weak against if ($tutorial) { foreach ($result->skills as $k => $v) { if (strpos($k, "weak") !== 0) { unset($result->skills[$k]); } } } // Finish calculating defense if ($result->defenseMultiplier <= 0) $result->defenseMultiplier = 1; $result->skillsList = implode(",", array_map(function($k, $v) { return $v == -1 ? $k : "$k:$v"; }, array_keys($result->skills), array_values($result->skills))); $result->health = $result->health * $result->defenseMultiplier + $result->defenseIncrement; sandbox_pvp_log("[PVP] $token merc $slot $merc->girl: health $result->health (x{$result->defenseMultiplier}, +{$result->defenseIncrement}) and $result->skillsList"); return $result; } function get_pocket_puppet_skills($level) { // Returns a random list of skills for a Pocket Puppet boss $candidates = [ "armored", "dodge", "guard", "immune against blade", "immune against blunt", "immune against mechanical", "immune against toxic", "loan shark", "misery", "money-grubber", "resists blade", "resists blunt", "resists mechanical", "fate", "timeskip", "energy drain", ]; $material_immunities = [ "immune against blade", "immune against blunt", "immune against mechanical" ]; $selected = []; $dice = rand(1, 3) + 2; while (count($selected) < $dice) { $index = array_rand($candidates); $skill = $candidates[$index]; unset($candidates[$index]); $selected[] = $skill; if (strpos($skill, "immune against") === 0) { $material = substr($skill, 15); $resists = "resists $material"; $candidates = array_diff($candidates, [$resists]); $selected = array_diff($selected, [$resists]); if (count(array_intersect($selected, $material_immunities)) == 2) { $remaining = array_diff($material_immunities, $selected); $candidates = array_diff($candidates, $remaining); } } } error_log("Pocket Puppet level $level has $dice skills: " . implode(",", $selected)); return implode(",", $selected); } function give_object($token, $code, $count) { if (!$count) $count = 1; $is_consumable = $code >= 9000; $info = $is_consumable ? get_consumable($code) : get_object($code); if (!$info) { error_log("PHP Error: Invalid consumable $code"); return; } for ($i = 0 ; $i < $count ; $i++) { if ($is_consumable) { add_inventory_object($token, new InventoryObject( player_token: $token, object_code: $code, slot: $info->slot, )); } else { if (!$info) $info = get_object($code); $shiny = make_shiny($info); add_inventory_object($token, new InventoryObject( player_token: $token, object_code: $code, slot: $info->slot, health: $info->health, energy: $info->energy, attack: $info->attack, defense: $info->defense, shiny: $shiny, skill: $info->skill )); if ($shiny) $info = null; } } clear_inventory_cache($token); } function give_objects_from_pool($token, $pool, $count, $reason) { $player = get_player_profile($token); if (!$count) $count = 1; $query = sql_multiple("select class_code, chance, girl from z_object_pool_list where pool_code = $pool"); $total = 0; $girls_total = 0; $girls = 0; $classes = []; $class_list = []; while ($row = $query->fetch_object()) { if ($row->girl) { $level = sql("select level from z_lust_progress where player_token = '$token' and girl = '$row->girl'"); $row->chance = $level ? $level : 1; $girls++; $girls_total += $level; } else { $total += (float)$row->chance; } $classes[] = $row; } if (!count($classes)) { echo "02 INVALID OBJECT POOL $pool\n"; error_log("Invalid object pool $pool: it has no class"); return; } $factor = $total > 0 ? (100 - $girls_total)/$total : 0; foreach ($classes as $class) { if (!$class->girl) $class->chance *= $factor; $class_list[] = "Class $class->class_code: $class->chance%"; } $keys_given = 0; $chips_given = 0; $chaos_cookies_given = 0; $cupid_cookies_given = 0; $marshmallows_given = 0; $shamrocks_given = 0; $tomoe_coins_given = 0; $naira_coins_given = 0; $coral_coins_given = 0; $fuel_given = 0; $log_list = ""; $rewards = []; for ($i = 0 ; $i < $count ; $i++) { $shiny = 0; $chance_value = rand(1, 100); // error_log("$token $player->name chance roll: $chance_value from pool $pool"); $selected_class = 0; foreach ($classes as $data) { $chance_value = $chance_value - $data->chance; if ($chance_value <= 0) { $selected_class = $data->class_code; // error_log("$token $player->name class roll results in $data->class_code"); break; } } if ($selected_class == 0) { // error_log("$token $player->name class roll outside bounds, using last class"); $selected_class = $classes[count($classes) - 1]->class_code; } $object_code = sql("select object_code from z_object_class_list where class_code = $selected_class order by rand() limit 1"); if (!$object_code) { echo "02 INVALID CLASS LIST $selected_class\n"; error_log("Invalid object class list $selected_class"); exit(); } $info = sql("select * from z_objects where code = $object_code"); if ($info) { $slot = $info->slot; $chance = 1; if (isset($result->shiny_chance) && $result->shiny_chance > 0) $chance = $result->shiny_chance; $shiny = make_shiny($info, $chance); add_inventory_object($token, new InventoryObject( player_token: $token, object_code: $object_code, slot: $slot, health: $info->health, energy: $info->energy, attack: $info->attack, defense: $info->defense, shiny: $shiny, skill: $info->skill )); if ($shiny) $object_code = "$object_code:0:$shiny"; } else { $info = sql("select * from z_lust_girls where code = $object_code"); $skin = (int)sql("select skin from z_object_class_list where class_code = $selected_class and object_code = $object_code"); if ($info) { $chance = 1; if (isset($result->shiny_chance) && $result->shiny_chance > 0) $chance = $result->shiny_chance; $shiny = make_shiny($info, $chance); add_inventory_merc($token, new InventoryMerc( girl: $info->id, attack: $info->attack, defense: $info->defense, energy: $info->energy, shiny: $shiny, rarity: $info->rarity, skin: $skin, evolutions: $info->evolutions )); if ($shiny) $object_code = "$object_code:0:$skin"; if ($skin) $object_code = "$object_code:$skin"; } else { $info = get_consumable($object_code); if ($object_code == 9012) $keys_given += 30; elseif ($object_code == 9013) $keys_given += 100; elseif ($object_code == 9015) $keys_given += 300; elseif ($object_code == 9016) $keys_given += 1000; elseif ($object_code == 9071) $keys_given += rand(1, 5); elseif ($object_code == 9112) $chips_given += 1; elseif ($object_code == 9113) $chips_given += 5; elseif ($object_code == 9154) $chaos_cookies_given += 1000; elseif ($object_code == 9155) $chaos_cookies_given += 3; elseif ($object_code == 9156) $chaos_cookies_given += 30; elseif ($object_code == 9210) $cupid_cookies_given += 30; elseif ($object_code == 9199) $marshmallows_given += 3; elseif ($object_code == 9200) $marshmallows_given += 30; elseif ($object_code == 9215) $shamrocks_given += 10; elseif ($object_code == 9216) $shamrocks_given += 50; elseif ($object_code == 9217) $shamrocks_given += 200; elseif ($object_code == 9243) $tomoe_coins_given += 30; elseif ($object_code == 9244) $tomoe_coins_given += 400; elseif ($object_code == 9252) $naira_coins_given += 30; elseif ($object_code == 9253) $naira_coins_given += 3; elseif ($object_code == 9261) $coral_coins_given += 30; elseif ($object_code == 9262) $coral_coins_given += 3; elseif ($info) { add_inventory_object($token, new InventoryObject( player_token: $token, object_code: $object_code, slot: $info->slot, )); } else { error_log("PHP Error: Unknown object code $object_code"); } } } $rewards[] = $object_code; if ($shiny) $log_list .= "\n - $object_code {$info->name} (shiny)"; else $log_list .= "\n - $object_code {$info->name}"; } if ($keys_given > 0) add_currency($token, $keys_given, "keys", $reason); if ($chips_given > 0) sql("update z_players set chips = chips + $chips_given where token = '$token'"); if ($fuel_given > 0) sql("update z_players set fuel = fuel + $fuel_given where token = '$token'"); if ($chaos_cookies_given > 0) add_currency($token, $chaos_cookies_given, "chaos_cookies", $reason); if ($cupid_cookies_given > 0) add_currency($token, $cupid_cookies_given, "cupid_cookies", $reason); if ($shamrocks_given > 0) add_currency($token, $shamrocks_given, "shamrocks", $reason); if ($marshmallows_given > 0) add_currency($token, $marshmallows_given, "marshmallows", $reason); if ($tomoe_coins_given > 0) add_currency($token, $tomoe_coins_given, "tomoe_coins", $reason); if ($naira_coins_given > 0) add_currency($token, $naira_coins_given, "naira_coins", $reason); if ($coral_coins_given > 0) add_currency($token, $coral_coins_given, "coral_coins", $reason); error_log("$token $player->name received $count objects from pool $pool: $log_list"); clear_inventory_cache($token); clear_player_cache($token); return $rewards; } function not_enough_currency($currency, $remaining, $required) { $currency = strtoupper($currency); switch ($currency) { case "MEDALS": echo "23 NOT ENOUGH MEDALS ($remaining ON HAND, $required REQUIRED)\n"; break; case "KEYS": case "`KEYS`": echo "27 NOT ENOUGH KEYS ($remaining ON HAND, $required REQUIRED)\n"; break; case "DOGTAGS": echo "30 NOT ENOUGH DOGTAGS ($remaining ON HAND, $required REQUIRED)\n"; break; case "FUEL": echo "49 NOT ENOUGH FUEL ($remaining ON HAND, $required REQUIRED)\n"; break; case "GEMS": echo "29 NOT ENOUGH GEMS ($remaining ON HAND, $required REQUIRED)\n"; break; case "DEADLY_COINS": echo "51 NOT ENOUGH DEADLY COINS ($remaining ON HAND, $required REQUIRED)\n"; break; case "CHAOS_COOKIES": echo "52 NOT ENOUGH CHAOS COOKIES ($remaining ON HAND, $required REQUIRED)\n"; break; case "MARSHMALLOWS": echo "55 NOT ENOUGH MARSHMALLOWS ($remaining ON HAND, $required REQUIRED)\n"; break; case "CUPID_COOKIES": echo "56 NOT ENOUGH CUPID COOKIES ($remaining ON HAND, $required REQUIRED)\n"; break; case "SHAMROCKS": echo "57 NOT ENOUGH SHAMROCKS ($remaining ON HAND, $required REQUIRED)\n"; break; case "BLACK_JEWELS": echo "53 NOT ENOUGH BLACK JEWELS ($remaining ON HAND, $required REQUIRED)\n"; break; case "JINGLE_BELLS": echo "54 NOT ENOUGH JINGLE BELLS ($remaining ON HAND, $required REQUIRED)\n"; break; case "TOMOE_COINS": echo "60 NOT ENOUGH TOMOE COINS ($remaining ON HAND, $required REQUIRED)\n"; break; case "NAIRA_COINS": echo "61 NOT ENOUGH NAIRA COINS ($remaining ON HAND, $required REQUIRED)\n"; break; case "CORAL_COINS": echo "63 NOT ENOUGH CORAL COINS ($remaining ON HAND, $required REQUIRED)\n"; break; case "DOUBLOONS": echo "58 NOT ENOUGH DOUBLOONS ($remaining ON HAND, $required REQUIRED)\n"; break; case "JEWELS": echo "35 NOT ENOUGH JEWELS ($remaining ON HAND, $required REQUIRED)\n"; break; default: echo "04 NOT ENOUGH $currency ($remaining ON HAND, $required REQUIRED)\n"; break; } } function get_event_factions($area_code) { $cached = apcu_fetch("event_factions_$area_code"); if ($cached !== false) return apcu_fetch("event_factions_$area_code"); $factions = null; $query = sql_multiple("select * from z_event_factions where area_code = $area_code"); while ($row = $query->fetch_object()) { if ($factions == null) $factions = []; // Hexadecimal range i.e 7-F $hex = $row->hex; if (strpos($hex, "-") !== false) { $range = explode("-", $hex); $start = hexdec($range[0]); $end = hexdec($range[1]); for ($i = $start ; $i <= $end ; $i++) $factions[$i] = $row->id; } else { error_log("PHP Error: get_event_factions: invalid hex range $hex in area $area_code faction $row->id"); } } apcu_store("event_factions_$area_code", $factions); return $factions; } function get_player_faction() { global $token; $area_code = get_active_event(); if ($area_code) { $factions = get_event_factions($area_code); if ($factions) { $hex = hexdec(substr($token, 0, 1)); if ($factions[$hex]) { return $factions[$hex]; } else { error_log("add_zombies_to_faction: hex $hex not in faction list"); return 0; } } } } function add_zombies_to_faction($server, $token, $zombies) { $area_code = get_active_event(); if ($area_code) { $factions = get_event_factions($area_code); if ($factions) { $hex = hexdec(substr($token, 0, 1)); if ($factions[$hex]) { $faction = $factions[$hex]; $own_zombies = (int)sql("select zombies_killed from z_event_faction_stats where area_code = $area_code and server = $server and faction_id = $factions[$hex]"); $foe_zombies = (int)sql("select sum(zombies_killed) from z_event_faction_stats where area_code = $area_code and server = $server and faction_id != $factions[$hex]"); // Make a multiplier based on the difference between own and foe zombies (make the faction with less zombies get more) $multiplier = 1; if ($own_zombies > 0) { $multiplier = 1 + ($foe_zombies - $own_zombies) / $own_zombies; if ($multiplier < 0.01) $multiplier = 0.01; } $zombies = round($zombies * $multiplier); // error_log("add_zombies_to_faction: server $server own $own_zombies, foe $foe_zombies, multiplier $multiplier, zombies $zombies"); sql("insert into z_event_faction_stats (server, area_code, faction_id, zombies_killed) values ($server, $area_code, $faction, $zombies) on duplicate key update zombies_killed = zombies_killed + $zombies"); return "$faction:$zombies"; } else { error_log("add_zombies_to_faction: hex $hex not in faction list"); } } } } function get_active_faction_stats($server) { if (!$server) return null; $stats = apcu_fetch("active_faction_stats_$server"); if ($stats === false) { $stats = null; $area_code = get_active_event(); if ($area_code) { $stats = []; $query = sql_multiple("select * from z_event_factions where area_code = $area_code"); while ($row = $query->fetch_object()) { if ($stats == null) $stats = []; $zombies_killed = (int)sql("select zombies_killed from z_event_faction_stats where area_code = $area_code and server = $server and faction_id = $row->id"); $stats[] = "$row->id,$row->description,$zombies_killed,$row->hex,$row->icon"; } $stats = implode(":", $stats); } apcu_store("active_faction_stats_$server", $stats, 10); } return $stats; } function add_currency($token, $amount, $currency, $reason = "") { $player_name = get_player_name($token); $amount = (int)$amount; $currency = str_replace("`", "", $currency); sql("update z_players set `$currency` = greatest(0, ifnull(`$currency`,0) + $amount) where token = '$token'"); if ($amount < 0 && ( $currency == "chaos_cookies" || $currency == "black_jewels" || $currency == "jingle_bells" || $currency == "deadly_coins" || $currency == "marshmallows" || $currency == "tomoe_coins")) { $increment = abs($amount); sql("update z_players set `{$currency}_spent` = `{$currency}_spent` + $increment where token = '$token'"); } $balance = (int)sql("select `$currency` from z_players where token = '$token'"); clear_player_cache($token); if ($amount > 0) { error_log("$token $player_name received $amount $currency (balance: $balance) $reason"); } else { error_log("$token $player_name lost " . abs($amount) . " $currency (balance: $balance) $reason"); } if ($currency != "money") { $reason = escape_sql_string($reason); sql("insert into z_accounting (player_token, currency, amount, balance, reason) values ('$token', '$currency', $amount, $balance, '$reason')"); } } function remove_currency($token, $amount, $currency, $reason = "") { $currency = str_replace("`", "", $currency); $balance = (int)sql("select `$currency` from z_players where token = '$token'"); if ($balance < $amount) { not_enough_currency($currency, $balance, $amount); $name = get_player_name($token); error_log("$token {$name} tried to buy $amount $currency but has only $balance"); exit(0); } add_currency($token, -$amount, $currency, $reason); } function get_player_banned($token) { $banned = apcu_fetch("player_banned_$token"); if ($banned === false) { $banned = (int)sql("select banned from z_players where token = '$token'"); apcu_store("player_banned_$token", $banned, 600); } return $banned; } function apply_vip_discount($shop, $tier, $price) { $discount = (int)get_game_variable("{$shop}_vip_discount_{$tier}"); if ($discount) { $price = floor($price * (1 - $discount / 100)); } return $price; } function give_pack_contents($token, $pack) { $player = get_player_profile($token); $result = get_pack($pack); $keys_given = rand($result->min_keys_given, $result->max_keys_given); $fuel_given = 0; $chips_given = 0; $deadly_coins_given = 0; $chaos_cookies_given = 0; $cupid_cookies_given = 0; $black_jewels_given = 0; $jingle_bells_given = 0; $tomoe_coins_given = 0; $marshmallows_given = 0; $shamrocks_given = 0; $doubloons_given = 0; $coral_coins_given = 0; $naira_coins_given = 0; $currency_amount = (int)rand($result->min_currency_given, $result->max_currency_given); $currency_given = $result->currency_given; if (!$currency_given) $currency_given = "none"; switch ($currency_given) { case 'chips': $chips_given += $currency_amount; break; case 'keys': $keys_given += $currency_amount; break; case 'fuel': $fuel_given += $currency_amount; break; case 'deadly_coins': $deadly_coins_given += $currency_amount; break; case 'chaos_cookies': $chaos_cookies_given += $currency_amount; break; case 'cupid_cookies': $cupid_cookies_given += $currency_amount; break; case 'jingle_bells': $jingle_bells_given += $currency_amount; break; case 'tomoe_coins': $tomoe_coins_given += $currency_amount; break; case 'black_jewels': $black_jewels_given += $currency_amount; break; case 'marshmallows': $marshmallows_given += $currency_amount; break; case 'shamrocks': $shamrocks_given += $currency_amount; break; case 'doubloons': $doubloons_given += $currency_amount; break; case 'coral_coins': $coral_coins_given += $currency_amount; break; case 'naira_coins': $naira_coins_given += $currency_amount; break; } $weapon_count = 0; $log_list = ""; $new_weapons = array(); foreach ($result->pools as $pack_line) { $count = $pack_line->count; $pool = $pack_line->pool_code; $query = sql_multiple("select class_code, chance, girl from z_object_pool_list where pool_code = $pool"); $total = 0; $girls_total = 0; $girls = 0; $classes = []; $class_list = []; while ($row = $query->fetch_object()) { if ($row->girl) { $level = sql("select level from z_lust_progress where player_token = '$token' and girl = '$row->girl'"); $row->chance = $level ? $level : 1; $girls++; $girls_total += $level; } else { $total += (float)$row->chance; } $classes[] = $row; } if (!count($classes)) { echo "02 INVALID OBJECT POOL $pool\n"; error_log("Invalid object pool $pool: it has no class"); return; } $factor = $total > 0 ? (100 - $girls_total)/$total : 0; foreach ($classes as $class) { if (!$class->girl) $class->chance *= $factor; $class_list[] = "Class $class->class_code: $class->chance%"; } error_log("$token $player->name Pack $pack pool $pool contains:\n" . join("\n", $class_list)); for ($i = 0 ; $i < $count ; $i++) { $shiny = 0; $chance_value = rand(1000, 100000) / 1000; $chance_roll = $chance_value; sandbox_log("$token $player->name Pack chance roll: $chance_value opening pack $pack and pool $pool"); $selected_class = 0; foreach ($classes as $data) { $chance_value = $chance_value - $data->chance; if ($chance_value <= 0) { $selected_class = $data->class_code; sandbox_log("$token $player->name Pack $pack class roll $chance_roll results in class $data->class_code"); break; } else { sandbox_log("$token $player->name Pack class roll $data->class_code not in range, remaining $chance_value"); } } if ($selected_class == 0) { // error_log("$token $player->name Pack class roll outside bounds, using last class"); $selected_class = $classes[count($classes) - 1]->class_code; } if ($player->level == 1) { // Tutorial hack $merc_girl = sql("select girl from z_mercs_inventory where player_token = '$token'"); $material = sql("select material from z_lust_girls where id = '$merc_girl'"); switch ($material) { case 1: $object_code = 352; break; case 2: $object_code = 501; break; case 3: $object_code = 445; break; default: error_log("PHP Error: $token $player->name Tutorial hack: unknown material $material for merc $merc_girl"); $object_code = 352; break; } error_log("$token $player->name Tutorial hack: giving weapon $object_code to merc $merc_girl (material $material)"); set_lust_progress("merchant", 24); } else { $object_code = sql("select object_code from z_object_class_list where class_code = $selected_class order by rand() limit 1"); if (!$object_code && array_key_exists($selected_class, $result->class_objects)) { $objects = $result->class_objects[$selected_class]; $object_code = $objects[array_rand($objects)]; error_log("$token $player->name Pack $pack class $selected_class choice results in object code $object_code"); } if (!$object_code) { echo "02 INVALID CLASS LIST $selected_class\n"; error_log("Invalid object class list $selected_class"); return; } } $info = sql("select * from z_objects where code = $object_code"); if ($info) { $slot = $info->slot; $chance = 1; if (isset($result->shiny_chance) && $result->shiny_chance > 0 && (int)$result->shiny_min_purchases <= $result->time) $chance = $result->shiny_chance; else if ($pack == 4 || $pack == 7) $chance = 2; sandbox_log("$token $player->name shiny chance: x$chance for object code $object_code (time {$result->time}, min purchases {$result->shiny_min_purchases})"); if ($player->level == 1) $chance = 100; $shiny = make_shiny($info, $chance); add_inventory_object($token, new InventoryObject( player_token: $token, object_code: $object_code, slot: $slot, health: $info->health, energy: $info->energy, attack: $info->attack, defense: $info->defense, shiny: $shiny, skill: $info->skill )); if ($shiny) $object_code = "$object_code:0:$shiny"; } else { $info = sql("select * from z_lust_girls where code = $object_code"); $skin = (int)sql("select skin from z_object_class_list where class_code = $selected_class and object_code = $object_code"); if ($info) { $chance = 1; if (isset($result->shiny_chance) && $result->shiny_chance > 0 && (int)$result->shiny_min_purchases <= $result->time) $chance = $result->shiny_chance; sandbox_log("$token $player->name shiny chance: x$chance for object code $object_code (time {$result->time}, min purchases {$result->shiny_min_purchases})"); $shiny = make_shiny($info, $chance); add_inventory_merc($token, new InventoryMerc( girl: $info->id, attack: $info->attack, defense: $info->defense, energy: $info->energy, shiny: $shiny, rarity: $info->rarity, skin: $skin, evolutions: $info->evolutions )); if ($shiny) $object_code = "$object_code:0:$shiny"; if ($skin) $object_code = "$object_code:$skin"; } else { $info = get_consumable($object_code); if ($object_code == 9012) $keys_given += 30; elseif ($object_code == 9013) $keys_given += 100; elseif ($object_code == 9071) $keys_given += rand(1, 5); elseif ($object_code == 9112) $chips_given += 1; elseif ($object_code == 9113) $chips_given += 5; elseif ($object_code == 9151) $deadly_coins_given += 1; elseif ($object_code == 9152) $deadly_coins_given += 10; elseif ($object_code == 9215) $shamrocks_given += 10; elseif ($object_code == 9216) $shamrocks_given += 50; elseif ($object_code == 9217) $shamrocks_given += 200; elseif ($info->action == "doubloons") $doubloons_given += $info->copies; elseif ($info) { add_inventory_object($token, new InventoryObject( player_token: $token, object_code: $object_code, slot: $info->slot, )); } else { error_log("PHP Error: Unknown object code $object_code"); } } } $new_weapons[] = $object_code; if ($shiny) $log_list .= "\n - $object_code {$info->name} (shiny)"; else $log_list .= "\n - $object_code {$info->name}"; } $weapon_count = $weapon_count + $count; } if ($keys_given > 0) add_currency($token, $keys_given, "`keys`", "Pack $pack"); if ($chips_given > 0) sql("update z_players set chips = chips + $chips_given where token = '$token'"); if ($fuel_given > 0) sql("update z_players set fuel = fuel + $fuel_given where token = '$token'"); if ($deadly_coins_given > 0) add_currency($token, $deadly_coins_given, "deadly_coins", "Pack $pack"); if ($chaos_cookies_given > 0) add_currency($token, $chaos_cookies_given, "chaos_cookies", "Pack $pack"); if ($cupid_cookies_given > 0) add_currency($token, $cupid_cookies_given, "cupid_cookies", "Pack $pack"); if ($jingle_bells_given > 0) add_currency($token, $jingle_bells_given, "jingle_bells", "Pack $pack"); if ($tomoe_coins_given > 0) add_currency($token, $tomoe_coins_given, "tomoe_coins", "Pack $pack"); if ($black_jewels_given > 0) add_currency($token, $black_jewels_given, "black_jewels", "Pack $pack"); if ($marshmallows_given > 0) add_currency($token, $marshmallows_given, "marshmallows", "Pack $pack"); if ($shamrocks_given > 0) add_currency($token, $shamrocks_given, "shamrocks", "Pack $pack"); if ($doubloons_given > 0) add_currency($token, $doubloons_given, "doubloons", "Pack $pack"); if ($coral_coins_given > 0) add_currency($token, $coral_coins_given, "coral_coins", "Pack $pack"); if ($naira_coins_given > 0) add_currency($token, $naira_coins_given, "naira_coins", "Pack $pack"); clear_player_cache($token); sql("insert into z_purchased_packs (player_token, code, last_purchase_date, count) values ('$token', $pack, now(), 1) on duplicate key update last_purchase_date = now(), count = count + 1"); /* Remove key and chip packs from inventory */ sql("delete from z_inventory where player_token = '$token' and object_code in (9012, 9013, 9014, 9015, 9016, 9112, 9113, 9154, 9155, 9156, 9186, 9199, 9200, 9215, 9216, 9217, 9218, 9219, 9220, 9243, 9244)"); if ($currency_amount) { $log_list .= "\n - $currency_amount $currency_given"; } return (object) compact( 'keys_given', 'chips_given', 'fuel_given', 'deadly_coins_given', 'chaos_cookies_given', 'cupid_cookies_given', 'black_jewels_given', 'jingle_bells_given', 'tomoe_coins_given', 'marshmallows_given', 'coral_coins_given', 'shamrocks_given', 'doubloons_given', 'new_weapons', 'log_list', 'currency_given', 'currency_amount' ); } function get_max_seen_area() { $value = apcu_fetch("max_seen_area"); if ($value != false) return $value; $max_area = sql("select code from z_mission_areas where released = 1 or finished = 1 or expires < now()"); if (!$max_area) { $max_area = sql("select code from z_mission_areas where hidden = 0 order by code desc limit 1"); } apcu_store("max_seen_area", $max_area); return $max_area; } 03 INVALID CLIENT VERSION Sorry, your game is too old!||||Please update it to the latest version and try again :)