--- title: Oggetti, giocatori ed entità layout: default root: ../.. idx: 3.4 description: Alla scopera degli ObjectRef degrad: level: warning title: Gradi e radianti message: La rotazione dell'oggetto figlio è in gradi, mentre quella dell'oggetto è in radianti. Assicurati di usare il metodo di misura corretto. --- ## Introduzione In questo capitolo imparerai come manipolare gli oggetti e come definirne di tuoi. - [Cosa sono gli oggetti, i giocatori e le entità?](#cosa-sono-gli-oggetti-i-giocatori-e-le-entità) - [Posizione e velocità](#posizione-e-velocità) - [Proprietà degli oggetti](#proprietà-degli-oggetti) - [Entità](#entità) - [Salute e danno](#salute-e-danno) - [Punti vita (HP)](#punti-vita-hp) - [Pugni, Gruppi Danno e Gruppi Armatura](#pugni-gruppi-danno-e-gruppi-armatura) - [Esempi di calcolo del danno](#esempi-di-calcolo-del-danno) - [Oggetti figli](#oggetti-figli) - [Il tuo turno](#il-tuo-turno) ## Cosa sono gli oggetti, i giocatori e le entità? Giocatori e entità sono entrambi tipi di oggetti (ObjectRef, quindi di nuovo un riferimento). Un oggetto è qualcosa che si può muovere indipendentemente dalla griglia di nodi e che ha proprietà come velocità e scala. Attenzione, tuttavia, a non confonderli con gli oggetti nel senso di "cose che possono essere messe in un inventario" (in inglese hanno infatti nomi diversi: *objects* e *items*), anche perché hanno un sistema di registrazione tutto loro. Ci sono alcune differenze tra giocatori ed entità. La più grande è che i primi sono controllati da chi gioca, mentre le seconde sono controllate dalle mod. Ciò significa che, per esempio, la velocità di un giocatore non può essere modificata dalle mod - i giocatori appartengono al lato client, mentre le entità al lato server. Un'altra differenza è che i giocatori fanno caricare i Blocchi Mappa che li circondano, le entità invece no: quest'ultime vengono salvate e diventano inattive quando il Blocco Mappa in cui si trovano viene rimosso dalla memoria. Questa distinzione è resa meno chiara dal fatto che le entità sono controllate tramite una Tabella di Entità Lua che vedremo qui sotto. ## Posizione e velocità `get_pos` e `set_pos` permettono di ottenere e impostare la posizione di un oggetto. ```lua local giocatore = core.get_player_by_name("bob") local pos = giocatore:get_pos() giocatore:set_pos({ x = pos.x, y = pos.y + 1, z = pos.z }) ``` `set_pos` imposta la posizione seduta stante, senza animazione. Se invece si desidera animare il movimento dell'oggetto verso la nuova posizione, si dovrebbe usare `move_to`. Questo, tuttavia, funziona soltanto per le entità. ```lua miaentita:move_to({ x = pos.x, y = pos.y + 1, z = pos.z }) ``` Una cosa importante da tenere a mente quando si lavora con le entità è la latenza di rete. In un mondo ideale, le informazioni riguardo i movimenti delle entità arriverebbero subito, nell'ordine corretto e a intervalli simili a come sono stati inviati. Tuttavia, a meno che tu non stia giocando in locale, questo non è un mondo ideale. Le informazioni ci mettono un attimo ad arrivare: per esempio i `set_pos` potrebbero non arrivare in ordine, saltando alcune chiamate. O lo spazio da coprire di un `move_to` potrebbe non essere suddiviso perfettamente, rendendo l'animazione meno fluida. Tutto ciò ha come risultato il client che vede cose leggermente diverse dal server, che è una cosa di cui dovresti essere consapevole. ## Proprietà degli oggetti Le proprietà degli oggetti sono usate per comunicare al client come renderizzare e gestire un oggetto. Non è possibile definire delle proprietà personalizzate, perché le proprietà sono per definizione fatte per essere usate dall'engine. Al contrario dei nodi, gli oggetti hanno un comportamento dinamico. Si può per esempio cambiare il loro aspetto in qualsiasi momento, aggiornandone le proprietà: ```lua oggetto:set_properties({ visual = "mesh", mesh = "omino.b3d", textures = {"omino_texture.png"}, visual_size = {x=1, y=1}, }) ``` Le proprietà aggiornate verranno inviate a tutti i giocatori nelle vicinanze. Questo è molto utile per avere una vasto ammontare di varietà a basso costo, uno fra tanti l'avere diverse skin per giocatore. Come mostrato nella prossima sezione, le entità possono avere delle proprietà iniziali, che andranno dichiarate nella loro definizione. ## Entità Un'entità ha una tabella di definizione che ricorda quella degli oggetti (intesi come *items*). Questa tabella può contenere metodi di callback, proprietà iniziali e membri personalizzati. ```lua local MiaEntita = { initial_properties = { hp_max = 1, physical = true, collide_with_objects = false, collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3}, visual = "wielditem", visual_size = {x = 0.4, y = 0.4}, textures = {""}, spritediv = {x = 1, y = 1}, initial_sprite_basepos = {x = 0, y = 0}, }, messaggio = "Messaggio predefinito", } function MiaEntita:imposta_messaggio(msg) self.messaggio = msg end ``` Tuttavia, c'è una differenza sostanziale tra entità e oggetti; perché quando un'entità appare (come quando viene creata o caricata) una nuova tabella viene generata per quell'entità, *ereditando* le proprietà dalla tabella originaria tramite una metatabella. Sia la tabella di un ObjectRef che quella di un'entità forniscono modi per ottenerne la controparte: ```lua local entita = oggetto:get_luaentity() local oggetto = entita.object print("L'entità si trova a " .. core.pos_to_string(oggetto:get_pos())) ``` Ci sono diversi callback disponibili da usare per le entità. Una lista completa può essere trovata in [lua_api.md](https://minetest.gitlab.io/minetest/minetest-namespace-reference/#registered-definition-tables). ```lua function MiaEntita:on_step(dtime) local pos = self.oggetto:get_pos() local pos_giu = vector.subtract(pos, vector.new(0, 1, 0)) local delta if core.get_node(pos_giu).name == "air" then delta = vector.new(0, -1, 0) elseif core.get_node(pos).name == "air" then delta = vector.new(0, 0, 1) else delta = vector.new(0, 1, 0) end delta = vector.multiply(delta, dtime) self.oggetto:move_to(vector.add(pos, delta)) end function MiaEntita:on_punch(hitter) core.chat_send_player(hitter:get_player_name(), self.message) end ``` Ora, se si volesse spawnare e usare questa entità, si noterà che il messaggio andrebbe perduto quando l'entità diventa inattiva per poi ritornare attiva. Questo succede perché il messaggio non è salvato. Al posto di salvare tutto nella tabella dell'entità, Minetest ti permette di scegliere come salvare le cose. Questo succede nella *Staticdata*, una stringa che contiene tutte le informazioni personalizzate che si vogliono ricordare. ```lua function MiaEntita:get_staticdata() return core.write_json({ messaggio = self.messaggio, }) end function MiaEntita:on_activate(staticdata, dtime_s) if staticdata ~= "" and staticdata ~= nil then local data = core.parse_json(staticdata) or {} self:imposta_messaggio(data.messaggio) end end ``` Minetest può chiamare `get_staticdata()` quando e quante volte vuole. Questo perché non aspetta che un Blocco Mappa diventi inattivo per salvarlo, in quanto comporterebbe una perdita di informazioni. I Blocchi Mappa sono salvati circa ogni 18 secondi, quindi dovresti notare un simile intervallo per la chiamata a `get_staticdata()`. `on_activate()`, d'altro canto, viene chiamato solo quando un'entità diventa attiva o nel Blocco Mappa appena caricato o quando spawna. Questo significa che il suo staticdata inizialmente potrebbe essere vuoto (dato l'intervallo di 18 secondi). Infine, c'è bisogno di registrare la tabella usando `register_entity`. ```lua core.register_entity("miamod:entita", MiaEntita) ``` L'entità può essere spawnata da una mod nel seguente modo: ```lua local pos = { x = 1, y = 2, z = 3 } local oggetto = core.add_entity(pos, "miamod:entita", nil) ``` Il terzo parametro è lo staticdata inziale. Per impostare il messaggio, puoi usare la Tabella di Entità Lua: ```lua oggetto:get_luaentity():imposta_messaggio("ciao!") ``` ## Salute e danno ### Punti vita (HP) Ogni oggetto ha un valore Punti Vita (HP), che rappresenta la salute attuale. Nei giocatori è inoltre possibile impostare il valore di salute massima tramite la proprietà `hp_max`. Al raggiungere gli 0 HP, un oggetto muore. ```lua local hp = oggetto:get_hp() oggetto:set_hp(hp + 3) ``` ### Pugni, Gruppi Danno e Gruppi Armatura Il danno è la riduzione degli HP di un oggetto. Quest'ultimo può prendere "a pugni" un altro oggetto per infliggere danno. "A pugni" perché non si parla necessariamente di un pugno vero e proprio: può essere infatti un'esplosione, un fendente, e via dicendo. Il danno complessivo è calcolato moltiplicando i Gruppi Danno del pugno con le vulnerabilità dell'obiettivo. Questo è poi eventualmente ridotto a seconda di quanto recente è stato il colpo precedente. Vedremo tra poco nel dettaglio quest'aspetto. Proprio come i [Gruppi Danno dei nodi](../items/nodes_items_crafting.html#strumenti-capacità-e-friabilità), questi gruppi possono prendere qualsiasi nome e non necessitano di essere registrati. Tuttavia, si è soliti usare gli stessi nomi di quelli dei nodi. La vulnerabilità di un oggetto a un certo tipo di danno dipende dalla sua [proprietà](#proprietà-degli-oggetti) `armor_groups`. Al contrario di quello che potrebbe far intendere il nome, `armor_groups` specifica la percentuale di danno subita da specifici Gruppi Danno, e non la resistenza. Se un Gruppo Danno non è elencato nei Gruppi Armatura di un oggetto, quest'ultimo ne sarà completamente immune. ```lua obiettivo:set_armor_groups({ fleshy = 90, crumbly = 50, }) ``` Nell'esempio qui sopra, l'oggetto subirà il 90% di danno `fleshy` e 50% di quello `crumbly`. Quando un giocatore prende "a pugni" un oggetto, i Gruppi Danno vengono estrapolati dall'oggetto che ha attualmente il mano. Negli altri casi, saranno le mod a decidere quali Gruppi Danno usare. ### Esempi di calcolo del danno Prendiamo a pugni l'oggetto `target`: ```lua local capacita_oggetto = { full_punch_interval = 0.8, damage_groups = { fleshy = 5, choppy = 10 }, -- Questo è usato solo per scavare nodi, ma è comunque richiesto max_drop_level=1, groupcaps={ fleshy={times={[1]=2.5, [2]=1.20, [3]=0.35}, uses=30, maxlevel=2}, }, } local tempo_da_ultimo_pugno = capacita_oggetto.full_punch_interval obiettivo:punch(oggetto, tempo_da_ultimo_pugno, capacita_oggetto) ``` Ora, calcoliamo a quanto ammonterà il danno. I Gruppi Danno del pugno sono `fleshy=5` e `choppy=10`, con l'obiettivo che prenderà 90% di danno da fleshy e 0% da choppy. Per prima cosa, moltiplichiamo i Gruppi Danno per le vulnerabilità, e ne sommiamo il risultato. Poi, moltiplichiamo per un numero tra 0 e 1 a seconda di `tempo_da_ultimo_pugno`. ```lua = (5*90/100 + 10*0/100) * limit(tempo_da_ultimo_pugno / full_punch_interval, 0, 1) = (5*90/100 + 10*0/100) * 1 = 4.5 ``` Dato che HP è un intero, il danno è arrotondato a 5 punti. ## Oggetti figli Gli oggetti figli (*attachments*) si muovono quando il genitore - l'oggetto al quale sono legati - viene mosso. Un oggetto può possedere un numero illimitato di figli, ma non più di un genitore. ```lua figlio:set_attach(parent, bone, position, rotation) ``` Il `get_pos()` di un oggetto ritornerà sempre la sua posizione globale, a prescindere dal fatto che sia figlio o meno. `set_attach` prende invece una posizione relativa, ma non è quello che credi: la posizione del figlio è relativa a quella del genitore *amplificata quest'ultima* di 10 volte. Quindi, `0,5,0` sarà metà nodo in alto rispetto al genitore. {% include notice.html notice=page.degrad %} Per i modelli 3D animati, il parametro `bone` (osso) è usato per collegare un'entità a un osso. Le animazioni 3D sono basate su degli scheletri - una rete di ossa nel modello dove ogni osso può avere una posizione e rotazione per cambiare il modello, tipo per muovere un braccio. Il collegamento a un osso è utile se si vuole per esempio far impugnare qualcosa al personaggio: ```lua oggetto:set_attach(player, "Braccio destro", -- osso predefinito {x=0.2, y=6.5, z=3}, -- posizione predefinita {x=-100, y=225, z=90}) -- rotazione predefinita ``` ## Il tuo turno * Fai un mulino combinando dei nodi con un'entità. * Crea un mostro di tua scelta (usando l'API delle entità, e senza usare altre mod).