monster_loot.php

main 148 lines · 4.2 KB Raw
Alex Alex Commit Initial commit 01/10/2026 09:20
1<?php require_once 'engine/init.php'; theme_open();
2
3/**
4 * Monster loot checker.
5 *
6 * Reads the same data the admin panel collects under Server Info: items.xml for
7 * the item names, and the monster folder for the loot trees. Flattens every
8 * monster's nested loot into plain rows, so the view only has to print them:
9 *
10 * $monsterLootError message when the data could not be read, else ''
11 * $itemList [item id => item name]
12 * $rarity [label => minimum percent]
13 * $monsterList one entry per monster:
14 * name monster name
15 * state 'loot' | 'empty' | 'failed'
16 * file the monster file, for the 'failed' message
17 * loot rows of [level, id, count, chance]
18 */
19
20// In percent (highest first).
21$rarity = array(
22 'Not Rare' => 7,
23 'Semi Rare' => 2,
24 'Rare' => 0.5,
25 'Very Rare' => 0
26);
27
28$monsterLootError = '';
29$itemList = array();
30$monsterList = array();
31
32/** Flatten one monster's nested <item> tree into rows. */
33function znote_flatten_loot($loot, int $level, array &$rows): void {
34 if (empty($loot)) {
35 return;
36 }
37
38 foreach ($loot as $entry) {
39 $chance = (float)$entry['chance'];
40 if (!$chance) {
41 $chance = (float)$entry['chance1'];
42 }
43
44 $rows[] = array(
45 'level' => $level,
46 'id' => (int)$entry['id'],
47 'count' => (int)$entry['countmax'],
48 'chance' => $chance / 1000,
49 );
50
51 // The nested <item> children of this entry - a bag's contents. Recursing
52 // per child and then reading that child's own ->item skipped a level,
53 // so bag contents never reached the page.
54 if (isset($entry->item)) {
55 znote_flatten_loot($entry->item, $level + 1, $rows);
56 }
57 }
58}
59
60// Parsing every monster file is far too slow to repeat per visitor, so the
61// flattened result is cached at rate 1 and the loot rate applied on the way
62// out. The admin panel drops this cache whenever items or monsters change.
63$cache = new Cache('engine/cache/monster_loot');
64$cache->useMemory(false);
65$cache->setExpiration(PHP_INT_MAX);
66
67$loaded = $cache->load();
68
69if (is_array($loaded) && isset($loaded['items'], $loaded['monsters'])) {
70
71 $itemList = $loaded['items'];
72 $monsterList = $loaded['monsters'];
73
74} else {
75
76 $itemsFile = serverdata_file('items.xml');
77 $items = is_file($itemsFile) ? @simplexml_load_file($itemsFile) : false;
78
79 if ($items === false) {
80 $monsterLootError = 'No items.xml yet. Upload one in the admin panel, under Server Info.';
81 } else {
82
83 foreach ($items->item as $item) {
84 $itemList[(int)$item['id']] = (string)$item['name'];
85 }
86
87 $source = serverdata_creature_source();
88 $monsters = is_file($source['index']) ? @simplexml_load_file($source['index']) : false;
89
90 if ($monsters === false) {
91 $monsterLootError = 'No monsters.xml at ' . $source['label'] . '. Upload your data/monster/ folder as a .zip in the admin panel, under Server Info.';
92 } else {
93
94 foreach ($monsters->monster as $monster) {
95 $file = (string)$monster['file'];
96 $loot = ($file !== '' && strpos($file, '..') === false)
97 ? @simplexml_load_file($source['dir'] . '/' . $file)
98 : false;
99
100 if ($loot === false) {
101 $monsterList[] = array(
102 'name' => (string)$monster['name'],
103 'state' => 'failed',
104 'file' => $file,
105 'loot' => array(),
106 );
107 continue;
108 }
109
110 $rows = array();
111 if (isset($loot->loot->item)) {
112 znote_flatten_loot($loot->loot->item, 1, $rows);
113 }
114
115 $monsterList[] = array(
116 'name' => (string)$monster['name'],
117 'state' => $rows ? 'loot' : 'empty',
118 'file' => $file,
119 'loot' => $rows,
120 );
121 }
122
123 $cache->setContent(array('items' => $itemList, 'monsters' => $monsterList));
124 $cache->save();
125 }
126 }
127}
128
129// The server's own loot rate, from the imported config.lua.
130if (isset($_GET['lootrate'])) {
131 $luaConfig = serverdata_load('config');
132 $lootRate = (is_array($luaConfig) && isset($luaConfig['rateLoot'])) ? (float)$luaConfig['rateLoot'] : 1;
133
134 if ($lootRate > 0 && $lootRate != 1) {
135 foreach ($monsterList as &$monster) {
136 foreach ($monster['loot'] as &$drop) {
137 $drop['chance'] *= $lootRate;
138 }
139 unset($drop);
140 }
141 unset($monster);
142 }
143}
144
145view('monster_loot');
146
147theme_close();
148
Top