842 lines
25 KiB
C++
842 lines
25 KiB
C++
/************************************************************************
|
|
* Minetest-c55
|
|
* Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
|
|
*
|
|
* content_mob.cpp
|
|
* Copyright (C) Lisa 'darkrose' Milne 2014 <lisa@ltmnet.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
************************************************************************/
|
|
|
|
#include "common.h"
|
|
#include "serverobject.h"
|
|
#include "content_sao.h"
|
|
#include "content_mob.h"
|
|
#include "content_list.h"
|
|
#include "content_craftitem.h"
|
|
#include "main.h"
|
|
#include "environment.h"
|
|
#include "map.h"
|
|
#include "profiler.h"
|
|
#include "intl.h"
|
|
|
|
MobFeatures g_content_mob_features[CONTENT_MOB_COUNT];
|
|
|
|
MobFeatures & content_mob_features(content_t c)
|
|
{
|
|
static MobFeatures ignore = MobFeatures();
|
|
if ((c&CONTENT_MOB_MASK) != CONTENT_MOB_MASK)
|
|
return ignore;
|
|
|
|
u16 i = (c&~CONTENT_MOB_MASK);
|
|
|
|
if (i >= CONTENT_MOB_COUNT)
|
|
return ignore;
|
|
|
|
if (g_content_mob_features[i].content != c)
|
|
return ignore;
|
|
|
|
return g_content_mob_features[i];
|
|
}
|
|
|
|
#ifndef SERVER
|
|
void MobFeatures::setBoxTexture(u16 i, std::string name, u8 alpha)
|
|
{
|
|
if (g_texturesource)
|
|
tiles[i].texture = g_texturesource->getTexture(name);
|
|
|
|
if (alpha != 255) {
|
|
tiles[i].alpha = alpha;
|
|
tiles[i].material_type = MATERIAL_ALPHA_VERTEX;
|
|
}
|
|
}
|
|
#endif
|
|
void MobFeatures::getAnimationFrames(MobAnimation type, int *start, int *end)
|
|
{
|
|
*start = 0;
|
|
*end = 0;
|
|
switch (type) {
|
|
case MA_STAND:
|
|
*start = animations[MA_STAND_START];
|
|
*end = animations[MA_STAND_END];
|
|
break;
|
|
case MA_MOVE:
|
|
*start = animations[MA_MOVE_START];
|
|
*end = animations[MA_MOVE_END];
|
|
break;
|
|
case MA_ATTACK:
|
|
*start = animations[MA_ATTACK_START];
|
|
*end = animations[MA_ATTACK_END];
|
|
break;
|
|
default:;
|
|
}
|
|
if (type != MA_STAND && start == end) {
|
|
*start = animations[MA_STAND_START];
|
|
*end = animations[MA_STAND_END];
|
|
}
|
|
}
|
|
void MobFeatures::setAnimationFrames(MobAnimation type, int start, int end)
|
|
{
|
|
switch (type) {
|
|
case MA_STAND:
|
|
animations[MA_STAND_START] = start;
|
|
animations[MA_STAND_END] = end;
|
|
break;
|
|
case MA_MOVE:
|
|
animations[MA_MOVE_START] = start;
|
|
animations[MA_MOVE_END] = end;
|
|
break;
|
|
case MA_ATTACK:
|
|
animations[MA_ATTACK_START] = start;
|
|
animations[MA_ATTACK_END] = end;
|
|
break;
|
|
default:;
|
|
}
|
|
}
|
|
|
|
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
|
|
|
bool content_mob_spawn(ServerEnvironment *env, v3s16 pos, u32 active_object_count)
|
|
{
|
|
ScopeProfiler sp(g_profiler, "SEnv: content_mob_spawn");
|
|
if (active_object_count > 20)
|
|
return false;
|
|
int rand = myrand();
|
|
if (pos.Y < 0 && rand%2 == 0)
|
|
return false;
|
|
assert(env);
|
|
Map *map = &env->getMap();
|
|
|
|
std::vector<content_t> can;
|
|
MapNode n = map->getNodeNoEx(pos);
|
|
MapNode a1 = map->getNodeNoEx(pos+v3s16(0,1,0));
|
|
MapNode a2 = map->getNodeNoEx(pos+v3s16(0,2,0));
|
|
content_t c0 = n.getContent();
|
|
content_t c1 = a1.getContent();
|
|
content_t c2 = a2.getContent();
|
|
|
|
if (c0 == CONTENT_IGNORE || c1 == CONTENT_IGNORE || c2 == CONTENT_IGNORE)
|
|
return false;
|
|
|
|
if ((c1 != CONTENT_AIR && c1 != CONTENT_WATERSOURCE) && c1 != c2)
|
|
return false;
|
|
|
|
for (u16 i=0; i<CONTENT_MOB_COUNT; i++) {
|
|
MobFeatures m = g_content_mob_features[i];
|
|
if (m.spawn_min_height > pos.Y)
|
|
continue;
|
|
if (m.spawn_max_height < pos.Y)
|
|
continue;
|
|
if (m.spawn_chance > 1 && rand%m.spawn_chance != 0)
|
|
continue;
|
|
can.push_back(i);
|
|
}
|
|
|
|
if (can.size() == 0)
|
|
return false;
|
|
|
|
MobFeatures m;
|
|
u32 index = 0;
|
|
|
|
if (can.size() > 1)
|
|
index = myrand_range(0,can.size()-1);
|
|
|
|
m = g_content_mob_features[can[index]];
|
|
|
|
if (m.content == CONTENT_IGNORE)
|
|
return false;
|
|
|
|
v3f p = intToFloat(pos+v3s16(0,1,0), BS);
|
|
vlprintf(CN_ACTION,"spawning mob (%u) at (%f,%f,%f)",m.content,pos.X,pos.Y+1,pos.Z);
|
|
ServerActiveObject *obj = new MobSAO(env, 0, p, m.content);
|
|
u16 id = env->addActiveObject(obj);
|
|
if (!id) {
|
|
vlprintf(CN_ACTION,"I lied, it didn't spawn");
|
|
return false;
|
|
}
|
|
|
|
if (m.sound_spawn != "")
|
|
env->addEnvEvent(ENV_EVENT_SOUND,p,m.sound_spawn);
|
|
|
|
return true;
|
|
}
|
|
|
|
void mob_spawn(v3s16 pos, content_t mob, ServerEnvironment *env)
|
|
{
|
|
|
|
MobFeatures &m = content_mob_features(mob);
|
|
|
|
if (m.content == CONTENT_IGNORE)
|
|
return;
|
|
if (!config_get_bool("world.game.mob.spawn.natural"))
|
|
return;
|
|
|
|
v3f p = intToFloat(pos+v3s16(0,1,0), BS);
|
|
vlprintf(CN_ACTION,"spawning mob (%u) at (%f,%f,%f)",m.content,pos.X,pos.Y+1,pos.Z);
|
|
ServerActiveObject *obj = new MobSAO(env, 0, p, m.content);
|
|
u16 id = env->addActiveObject(obj);
|
|
if (!id) {
|
|
vlprintf(CN_ACTION,"I lied, it didn't spawn");
|
|
return;
|
|
}
|
|
|
|
if (id && m.sound_spawn != "")
|
|
env->addEnvEvent(ENV_EVENT_SOUND,p,m.sound_spawn);
|
|
}
|
|
|
|
void mob_spawn_passive(v3s16 pos, bool water, ServerEnvironment *env)
|
|
{
|
|
std::vector<content_t> can;
|
|
int rand = myrand();
|
|
for (u16 i=0; i<CONTENT_MOB_COUNT; i++) {
|
|
MobFeatures m = g_content_mob_features[i];
|
|
if (!m.spawn_naturally)
|
|
continue;
|
|
if (m.level > MOB_PASSIVE)
|
|
continue;
|
|
if (m.spawn_water != water)
|
|
continue;
|
|
if (m.spawn_chance > 1 && rand%m.spawn_chance != 0)
|
|
continue;
|
|
can.push_back(i);
|
|
}
|
|
|
|
if (can.size() == 0)
|
|
return;
|
|
|
|
MobFeatures m;
|
|
u32 index = 0;
|
|
|
|
if (can.size() > 1)
|
|
index = myrand_range(0,can.size()-1);
|
|
|
|
m = g_content_mob_features[can[index]];
|
|
|
|
if (m.content == CONTENT_IGNORE)
|
|
return;
|
|
|
|
v3s16 spots[9] = {
|
|
v3s16(0,0,0),
|
|
v3s16(-2,0,0),
|
|
v3s16(-2,0,2),
|
|
v3s16(-2,0,-2),
|
|
v3s16(2,0,0),
|
|
v3s16(2,0,-2),
|
|
v3s16(2,0,2),
|
|
v3s16(0,0,-2),
|
|
v3s16(0,0,2)
|
|
};
|
|
u16 cnt = 0;
|
|
for (u16 i=0; i<9 && cnt < m.spawn_group; i++) {
|
|
v3s16 p = pos+spots[i];
|
|
if (water) {
|
|
if (env->getMap().getNodeNoEx(p+v3s16(0,1,0)).getContent() != CONTENT_WATERSOURCE)
|
|
continue;
|
|
}else{
|
|
bool have_spot = false;
|
|
for (s16 x=-1; !have_spot && x<2; x++) {
|
|
for (s16 y=-1; !have_spot && y<2; y++) {
|
|
for (s16 z=-1; !have_spot && z<2; z++) {
|
|
v3s16 pp = p+v3s16(x,y,z);
|
|
if (content_features(env->getMap().getNodeNoEx(pp).getContent()).draw_type != CDT_DIRTLIKE)
|
|
continue;
|
|
pp.Y++;
|
|
if (!content_features(env->getMap().getNodeNoEx(pp).getContent()).air_equivalent)
|
|
continue;
|
|
pp.Y++;
|
|
if (!content_features(env->getMap().getNodeNoEx(pp).getContent()).air_equivalent)
|
|
continue;
|
|
p += v3s16(x,y,z);
|
|
have_spot = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!have_spot)
|
|
continue;
|
|
}
|
|
mob_spawn(p,m.content,env);
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
void mob_spawn_hostile(v3s16 pos, bool water, ServerEnvironment *env)
|
|
{
|
|
std::vector<content_t> can;
|
|
u8 level = mobLevelI(config_get("world.game.mob.spawn.level"));
|
|
if (level < MOB_AGGRESSIVE)
|
|
return;
|
|
for (u16 i=0; i<CONTENT_MOB_COUNT; i++) {
|
|
MobFeatures m = g_content_mob_features[i];
|
|
if (!m.spawn_naturally)
|
|
continue;
|
|
if (m.level < MOB_AGGRESSIVE)
|
|
continue;
|
|
if (m.level > level)
|
|
continue;
|
|
if (m.spawn_water != water)
|
|
continue;
|
|
can.push_back(i);
|
|
}
|
|
|
|
if (can.size() == 0)
|
|
return;
|
|
|
|
MobFeatures m;
|
|
u32 index = 0;
|
|
|
|
if (can.size() > 1)
|
|
index = myrand_range(0,can.size()-1);
|
|
|
|
m = g_content_mob_features[can[index]];
|
|
|
|
if (m.content == CONTENT_IGNORE)
|
|
return;
|
|
|
|
v3s16 spots[9] = {
|
|
v3s16(0,0,0),
|
|
v3s16(-2,0,0),
|
|
v3s16(-2,0,2),
|
|
v3s16(-2,0,-2),
|
|
v3s16(2,0,0),
|
|
v3s16(2,0,-2),
|
|
v3s16(2,0,2),
|
|
v3s16(0,0,-2),
|
|
v3s16(0,0,2)
|
|
};
|
|
u16 cnt = 0;
|
|
for (u16 i=0; i<9 && cnt < m.spawn_group; i++) {
|
|
v3s16 p = pos+spots[i];
|
|
if (water) {
|
|
if (env->getMap().getNodeNoEx(p+v3s16(0,1,0)).getContent() != CONTENT_WATERSOURCE)
|
|
continue;
|
|
}else{
|
|
content_t c = env->getMap().getNodeNoEx(pos).getContent();
|
|
bool have_spot = false;
|
|
for (s16 x=-1; !have_spot && x<2; x++) {
|
|
for (s16 y=-1; !have_spot && y<2; y++) {
|
|
for (s16 z=-1; !have_spot && z<2; z++) {
|
|
v3s16 pp = p+v3s16(x,y,z);
|
|
if (env->getMap().getNodeNoEx(pp).getContent() != c)
|
|
continue;
|
|
pp.Y++;
|
|
if (env->getMap().getNodeNoEx(pp).getContent() != CONTENT_AIR)
|
|
continue;
|
|
pp.Y++;
|
|
if (env->getMap().getNodeNoEx(pp).getContent() != CONTENT_AIR)
|
|
continue;
|
|
p += v3s16(x,y,z);
|
|
have_spot = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!have_spot)
|
|
continue;
|
|
}
|
|
mob_spawn(p,m.content,env);
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
void content_mob_init()
|
|
{
|
|
content_t i;
|
|
MobFeatures *f = NULL;
|
|
|
|
i = CONTENT_MOB_RAT;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Rat");
|
|
f->level = MOB_PASSIVE;
|
|
f->model = "rat.x";
|
|
f->setTexture("mob_rat.png");
|
|
f->setAnimationFrames(MA_STAND,0,79);
|
|
f->punch_action = MPA_PICKUP;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_RAT)+" 1";
|
|
f->motion = MM_WANDER;
|
|
f->angry_motion = MM_FLEE;
|
|
f->moves_silently = true;
|
|
f->spawn_max_height = -5;
|
|
f->spawn_group = 3;
|
|
f->spawn_naturally = true;
|
|
f->setCollisionBox(aabb3f(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_FIREFLY;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Firefly");
|
|
f->level = MOB_PASSIVE;
|
|
f->model_scale = v3f(0.5,0.5,0.5);
|
|
f->setTexture("mob_firefly.png");
|
|
f->punch_action = MPA_PICKUP;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_FIREFLY)+" 1";
|
|
f->motion = MM_WANDER;
|
|
f->motion_type = MMT_FLYLOW;
|
|
f->glow_light = LIGHT_MAX-1;
|
|
f->moves_silently = true;
|
|
f->spawn_min_height = -5;
|
|
f->spawn_max_height = 50;
|
|
f->spawn_naturally = false;
|
|
f->setCollisionBox(aabb3f(-BS/4.,-BS/6.,-BS/4., BS/4.,BS/6.,BS/4.));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_OERKKI;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Oerkki");
|
|
f->level = MOB_AGGRESSIVE;
|
|
f->model = "oerkki.x";
|
|
f->model_scale = v3f(4,4,4);
|
|
f->setTexture("mob_oerkki.png");
|
|
f->setAnimationFrames(MA_STAND,24,36);
|
|
f->setAnimationFrames(MA_ATTACK,37,49);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_OERKKI_DUST)+" 2";
|
|
f->motion = MM_SEEKER;
|
|
f->moves_silently = true;
|
|
f->spawn_max_height = 2;
|
|
f->sound_spawn = "mob-oerkki-spawn";
|
|
f->attack_player_damage = 15;
|
|
f->attack_player_range = v3f(1,1,1);
|
|
f->setCollisionBox(aabb3f(-BS/3.,0.0,-BS/3., BS/3.,BS*2.,BS/3.));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_DUNGEON_MASTER;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Dungeon Master");
|
|
f->level = MOB_DESTRUCTIVE;
|
|
f->model = "dungeon_master.b3d";
|
|
f->model_rotation = v3f(0,-90,0);
|
|
f->model_offset = v3f(0,1.0,0);
|
|
f->setTexture("mob_dungeon_master.png");
|
|
f->setAnimationFrames(MA_STAND,1,30);
|
|
f->setAnimationFrames(MA_MOVE,31,60);
|
|
f->setAnimationFrames(MA_ATTACK,61,90);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_GUNPOWDER)+" 4";
|
|
f->motion = MM_SENTRY;
|
|
f->attack_throw_object = CONTENT_MOB_FIREBALL;
|
|
f->attack_glow_light = LIGHT_MAX-1;
|
|
f->attack_throw_offset = v3f(0,1.4,-1.0);
|
|
f->setCollisionBox(aabb3f(-0.75*BS, 0.*BS, -0.75*BS, 0.75*BS, 2.0*BS, 0.75*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_FIREBALL;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Fireball");
|
|
f->level = MOB_DESTRUCTIVE;
|
|
f->setTexture("mob_fireball.png");
|
|
f->punch_action = MPA_IGNORE;
|
|
f->motion = MM_CONSTANT;
|
|
f->motion_type = MMT_FLY;
|
|
f->glow_light = LIGHT_MAX-1;
|
|
f->moves_silently = true;
|
|
f->attack_player_damage = 15;
|
|
f->attack_player_range = v3f(2,2,2);
|
|
f->contact_explosion_diameter = 3;
|
|
f->spawn_naturally = false;
|
|
f->setCollisionBox(aabb3f(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.));
|
|
|
|
i = CONTENT_MOB_DOE;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Doe");
|
|
f->level = MOB_PASSIVE;
|
|
f->hp = 30;
|
|
f->model = "doe.x";
|
|
f->model_scale = v3f(1.0,1.0,1.0);
|
|
f->model_rotation = v3f(0,180,0);
|
|
f->model_offset = v3f(0,0,0);
|
|
f->setTexture("mob_doe.png");
|
|
f->setAnimationFrames(MA_STAND,0,240);
|
|
f->setAnimationFrames(MA_MOVE,241,300);
|
|
f->setAnimationFrames(MA_ATTACK,241,300);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_FUR)+" 2";
|
|
f->motion = MM_WANDER;
|
|
f->angry_motion = MM_FLEE;
|
|
f->motion_type = MMT_WALK;
|
|
f->follow_item = CONTENT_CRAFTITEM_WHEAT;
|
|
f->sound_random = "mob-deer-env";
|
|
f->spawn_min_height = -5;
|
|
f->spawn_max_height = 40;
|
|
f->spawn_group = 3;
|
|
f->setCollisionBox(aabb3f(-0.6*BS, 0., -0.6*BS, 0.6*BS, 1.25*BS, 0.6*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_STAG;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Stag");
|
|
f->level = MOB_AGGRESSIVE;
|
|
f->hp = 40;
|
|
f->model = "stag.x";
|
|
f->model_scale = v3f(1.1,1.1,1.1);
|
|
f->model_rotation = v3f(0,180,0);
|
|
f->model_offset = v3f(0,0,0);
|
|
f->setTexture("mob_doe.png");
|
|
f->setAnimationFrames(MA_STAND,0,240);
|
|
f->setAnimationFrames(MA_MOVE,241,300);
|
|
f->setAnimationFrames(MA_ATTACK,301,324);
|
|
f->punch_action = MPA_HARM;
|
|
f->tamed_mob = CONTENT_MOB_TAMESTAG;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_MEAT)+" 2";
|
|
f->motion = MM_WANDER;
|
|
f->motion_type = MMT_WALK;
|
|
f->angry_motion = MM_SEEKER;
|
|
f->sound_random = "mob-deer-env";
|
|
f->spawn_min_height = -5;
|
|
f->spawn_max_height = 40;
|
|
f->spawn_chance = 2;
|
|
f->attack_player_damage = 15;
|
|
f->attack_player_range = v3f(1,1,1);
|
|
f->setCollisionBox(aabb3f(-0.7*BS, 0., -0.7*BS, 0.7*BS, 1.5*BS, 0.7*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_TAMESTAG;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Stag");
|
|
f->level = MOB_PASSIVE;
|
|
f->hp = 40;
|
|
f->model = "stag.b3d";
|
|
f->model_scale = v3f(1,1,1);
|
|
f->model_rotation = v3f(0,-90,0);
|
|
f->model_offset = v3f(0,1.1,0);
|
|
f->setTexture("mob_tamestag.png");
|
|
f->setAnimationFrames(MA_STAND,61,120);
|
|
f->setAnimationFrames(MA_MOVE,0,60);
|
|
f->setAnimationFrames(MA_ATTACK,0,60);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_FUR)+" 2";
|
|
f->motion = MM_SEEKER;
|
|
f->angry_motion = MM_FLEE;
|
|
f->motion_type = MMT_WALK;
|
|
f->sound_random = "mob-deer-env";
|
|
f->spawn_naturally = false;
|
|
f->setCollisionBox(aabb3f(-0.7*BS, 0., -0.7*BS, 0.7*BS, 1.5*BS, 0.7*BS));
|
|
|
|
i = CONTENT_MOB_FISH;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Fish");
|
|
f->level = MOB_PASSIVE;
|
|
f->model = "fish.b3d";
|
|
f->model_rotation = v3f(0,-90,0);
|
|
f->model_offset = v3f(0,0.5,0);
|
|
f->setTexture("mob_fish.png");
|
|
f->setAnimationFrames(MA_STAND,1,80);
|
|
f->setAnimationFrames(MA_MOVE,81,155);
|
|
f->punch_action = MPA_HARM;
|
|
f->special_punch_item = TT_SPEAR;
|
|
f->special_dropped_item = CONTENT_CRAFTITEM_FISH;
|
|
f->special_dropped_count = 1;
|
|
f->special_dropped_max = 0;
|
|
f->motion = MM_WANDER;
|
|
f->angry_motion = MM_FLEE;
|
|
f->motion_type = MMT_SWIM;
|
|
f->moves_silently = true;
|
|
f->spawn_group = 3;
|
|
f->spawn_water = true;
|
|
f->hp = 5;
|
|
f->setCollisionBox(aabb3f(-0.25*BS, 0.25*BS, -0.25*BS, 0.25*BS, 0.75*BS, 0.25*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_SHARK;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Shark");
|
|
f->level = MOB_AGGRESSIVE;
|
|
f->hp = 40;
|
|
f->model = "shark.b3d";
|
|
f->model_scale = v3f(1,1,1);
|
|
f->model_rotation = v3f(0,-90,0);
|
|
f->model_offset = v3f(0,0.5,0);
|
|
f->setTexture("mob_shark.png");
|
|
f->setAnimationFrames(MA_STAND,1,80);
|
|
f->setAnimationFrames(MA_MOVE,80,160);
|
|
f->setAnimationFrames(MA_ATTACK,80,160);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_MEAT)+" 2";
|
|
f->motion = MM_SEEKER;
|
|
f->motion_type = MMT_SWIM;
|
|
f->moves_silently = true;
|
|
f->spawn_water = true;
|
|
f->attack_player_damage = 15;
|
|
f->attack_player_range = v3f(1,1,1);
|
|
f->setCollisionBox(aabb3f(-0.75*BS, 0., -0.75*BS, 0.75*BS, 1.*BS, 0.75*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_WOLF;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Wolf");
|
|
f->level = MOB_AGGRESSIVE;
|
|
f->hp = 40;
|
|
f->model = "wolf.b3d";
|
|
f->model_scale = v3f(1,1,1);
|
|
f->model_rotation = v3f(0,-90,0);
|
|
f->model_offset = v3f(0,0.5,0);
|
|
f->setTexture("mob_wolf.png");
|
|
f->setAnimationFrames(MA_STAND,1,60);
|
|
f->setAnimationFrames(MA_MOVE,61,120);
|
|
f->setAnimationFrames(MA_ATTACK,61,120);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_MEAT)+" 2";
|
|
f->tamed_mob = CONTENT_MOB_TAMEWOLF;
|
|
f->motion = MM_SEEKER;
|
|
f->motion_type = MMT_WALK;
|
|
f->spawn_min_height = 0;
|
|
f->spawn_max_height = 40;
|
|
f->sound_punch = "mob-wolf-hit";
|
|
f->sound_spawn = "mob-wolf-spawn";
|
|
f->attack_player_damage = 15;
|
|
f->attack_player_range = v3f(1,1,1);
|
|
f->setCollisionBox(aabb3f(-0.5*BS, 0., -0.5*BS, 0.5*BS, 1.*BS, 0.5*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_TAMEWOLF;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Tame Wolf");
|
|
f->level = MOB_PASSIVE;
|
|
f->hp = 40;
|
|
f->model = "wolf.b3d";
|
|
f->model_scale = v3f(1,1,1);
|
|
f->model_rotation = v3f(0,-90,0);
|
|
f->model_offset = v3f(0,0.5,0);
|
|
f->setTexture("mob_tamewolf.png");
|
|
f->setAnimationFrames(MA_STAND,1,60);
|
|
f->setAnimationFrames(MA_MOVE,61,120);
|
|
f->setAnimationFrames(MA_ATTACK,61,120);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_FUR)+" 2";
|
|
f->motion = MM_SEEKER;
|
|
f->motion_type = MMT_WALK;
|
|
f->angry_motion = MM_FLEE;
|
|
f->sound_punch = "mob-wolf-hit";
|
|
f->attack_mob_damage = 5;
|
|
f->attack_mob_range = v3f(1,1,1);
|
|
f->spawn_naturally = false;
|
|
f->setCollisionBox(aabb3f(-0.5*BS, 0., -0.5*BS, 0.5*BS, 1.*BS, 0.5*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_SHEEP;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Sheep");
|
|
f->level = MOB_PASSIVE;
|
|
f->hp = 30;
|
|
f->model = "sheep.b3d";
|
|
f->model_scale = v3f(0.8,0.8,0.8);
|
|
f->model_rotation = v3f(0,180,0);
|
|
f->model_offset = v3f(0,0.6,0);
|
|
f->setTexture("mob_sheep.png");
|
|
f->setAnimationFrames(MA_STAND,40,60);
|
|
f->setAnimationFrames(MA_MOVE,1,28);
|
|
f->setAnimationFrames(MA_ATTACK,1,28);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_MEAT)+" 2";
|
|
f->special_punch_item = TT_SHEAR;
|
|
f->special_dropped_item = CONTENT_CRAFTITEM_STRING;
|
|
f->special_dropped_count = 4;
|
|
f->special_dropped_max = 8;
|
|
f->motion = MM_WANDER;
|
|
f->angry_motion = MM_FLEE;
|
|
f->motion_type = MMT_WALK;
|
|
f->follow_item = CONTENT_CRAFTITEM_WHEAT;
|
|
f->sound_random = "mob-sheep-env";
|
|
f->sound_random_extra = "mob-ducksheep-env";
|
|
f->spawn_min_height = 2;
|
|
f->spawn_max_height = 50;
|
|
f->spawn_group = 4;
|
|
f->setCollisionBox(aabb3f(-0.4*BS, 0., -0.4*BS, 0.4*BS, 1.*BS, 0.4*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_SHEARED_SHEEP;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Sheared Sheep");
|
|
f->level = MOB_PASSIVE;
|
|
f->hp = 30;
|
|
f->model = "sheared_sheep.b3d";
|
|
f->model_scale = v3f(0.8,0.8,0.8);
|
|
f->model_rotation = v3f(0,180,0);
|
|
f->model_offset = v3f(0,0.6,0);
|
|
f->setTexture("mob_sheared_sheep.png");
|
|
f->setAnimationFrames(MA_STAND,40,60);
|
|
f->setAnimationFrames(MA_MOVE,1,28);
|
|
f->setAnimationFrames(MA_ATTACK,1,28);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_MEAT)+" 2";
|
|
f->motion = MM_WANDER;
|
|
f->angry_motion = MM_FLEE;
|
|
f->motion_type = MMT_WALK;
|
|
f->sound_random = "mob-sheep-env";
|
|
f->sound_random_extra = "mob-ducksheep-env";
|
|
f->spawn_naturally = false;
|
|
f->setCollisionBox(aabb3f(-0.4*BS, 0., -0.4*BS, 0.4*BS, 1.*BS, 0.4*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_SNOWBALL;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Snowball");
|
|
f->level = MOB_AGGRESSIVE;
|
|
f->setTexture("snow_ball.png");
|
|
f->model_offset = v3f(0,0.2,0);
|
|
f->punch_action = MPA_IGNORE;
|
|
f->motion = MM_THROWN;
|
|
f->motion_type = MMT_FLY;
|
|
f->moves_silently = true;
|
|
f->attack_mob_damage = 5;
|
|
f->attack_mob_range = v3f(1,1,1);
|
|
f->contact_place_node = CONTENT_SNOW;
|
|
f->contact_drop_item = CONTENT_CRAFTITEM_SNOW_BALL;
|
|
f->spawn_naturally = false;
|
|
f->setCollisionBox(aabb3f(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.));
|
|
|
|
i = CONTENT_MOB_ARROW;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Arrow");
|
|
f->level = MOB_AGGRESSIVE;
|
|
f->setTexture("mob_arrow.png");
|
|
f->texture_display = MDT_EXTRUDED;
|
|
f->model_offset = v3f(0,0.2,0);
|
|
f->punch_action = MPA_IGNORE;
|
|
f->motion = MM_THROWN;
|
|
f->motion_type = MMT_FLY;
|
|
f->moves_silently = true;
|
|
f->attack_mob_damage = 20;
|
|
f->attack_mob_range = v3f(1,1,1);
|
|
f->contact_drop_item = CONTENT_CRAFTITEM_ARROW;
|
|
f->spawn_naturally = false;
|
|
f->setCollisionBox(aabb3f(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.));
|
|
|
|
i = CONTENT_MOB_GREY_KITTY;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Grey Kitten");
|
|
f->level = MOB_AGGRESSIVE;
|
|
f->hp = 30;
|
|
f->model = "kitty.b3d";
|
|
f->model_scale = v3f(1.0,1.0,1.0);
|
|
f->model_rotation = v3f(0,180,0);
|
|
f->model_offset = v3f(0,0,0);
|
|
f->setTexture("mob_kitty_greytabby.png");
|
|
f->setAnimationFrames(MA_STAND,90,110);
|
|
f->setAnimationFrames(MA_MOVE,0,50);
|
|
f->setAnimationFrames(MA_ATTACK,110,190);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_FUR)+" 1";
|
|
f->motion = MM_WANDER;
|
|
f->motion_type = MMT_WALK;
|
|
f->angry_motion = MM_SEEKER;
|
|
f->sound_random = "mob-kitty-env";
|
|
f->spawn_min_height = -5;
|
|
f->spawn_max_height = 40;
|
|
f->attack_player_damage = 1;
|
|
f->attack_player_range = v3f(1,1,1);
|
|
f->setCollisionBox(aabb3f(-0.6*BS, 0., -0.6*BS, 0.6*BS, 1.25*BS, 0.6*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_WHITE_KITTY;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("White Kitten");
|
|
f->level = MOB_AGGRESSIVE;
|
|
f->hp = 30;
|
|
f->model = "kitty.b3d";
|
|
f->model_scale = v3f(1.0,1.0,1.0);
|
|
f->model_rotation = v3f(0,180,0);
|
|
f->model_offset = v3f(0,0,0);
|
|
f->setTexture("mob_kitty_white.png");
|
|
f->setAnimationFrames(MA_STAND,90,110);
|
|
f->setAnimationFrames(MA_MOVE,0,50);
|
|
f->setAnimationFrames(MA_ATTACK,110,190);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_FUR)+" 1";
|
|
f->motion = MM_WANDER;
|
|
f->motion_type = MMT_WALK;
|
|
f->angry_motion = MM_SEEKER;
|
|
f->sound_random = "mob-kitty-env";
|
|
f->spawn_min_height = -5;
|
|
f->spawn_max_height = 40;
|
|
f->attack_player_damage = 1;
|
|
f->attack_player_range = v3f(1,1,1);
|
|
f->setCollisionBox(aabb3f(-0.6*BS, 0., -0.6*BS, 0.6*BS, 1.25*BS, 0.6*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_SIAMESE_KITTY;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Siamese Kitten");
|
|
f->level = MOB_AGGRESSIVE;
|
|
f->hp = 30;
|
|
f->model = "kitty.b3d";
|
|
f->model_scale = v3f(1.0,1.0,1.0);
|
|
f->model_rotation = v3f(0,180,0);
|
|
f->model_offset = v3f(0,0,0);
|
|
f->setTexture("mob_kitty_siamese.png");
|
|
f->setAnimationFrames(MA_STAND,90,110);
|
|
f->setAnimationFrames(MA_MOVE,0,50);
|
|
f->setAnimationFrames(MA_ATTACK,110,190);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_FUR)+" 1";
|
|
f->motion = MM_WANDER;
|
|
f->motion_type = MMT_WALK;
|
|
f->angry_motion = MM_SEEKER;
|
|
f->sound_random = "mob-kitty-env";
|
|
f->spawn_min_height = -5;
|
|
f->spawn_max_height = 40;
|
|
f->attack_player_damage = 1;
|
|
f->attack_player_range = v3f(1,1,1);
|
|
f->setCollisionBox(aabb3f(-0.6*BS, 0., -0.6*BS, 0.6*BS, 1.25*BS, 0.6*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
|
|
i = CONTENT_MOB_GINGER_KITTY;
|
|
f = &g_content_mob_features[i&~CONTENT_MOB_MASK];
|
|
f->content = i;
|
|
f->description = gettext("Ginger Kitten");
|
|
f->level = MOB_AGGRESSIVE;
|
|
f->hp = 30;
|
|
f->model = "kitty.b3d";
|
|
f->model_scale = v3f(1.0,1.0,1.0);
|
|
f->model_rotation = v3f(0,180,0);
|
|
f->model_offset = v3f(0,0,0);
|
|
f->setTexture("mob_kitty_gingertabby.png");
|
|
f->setAnimationFrames(MA_STAND,90,110);
|
|
f->setAnimationFrames(MA_MOVE,0,50);
|
|
f->setAnimationFrames(MA_ATTACK,110,190);
|
|
f->punch_action = MPA_HARM;
|
|
f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_FUR)+" 1";
|
|
f->motion = MM_WANDER;
|
|
f->motion_type = MMT_WALK;
|
|
f->angry_motion = MM_SEEKER;
|
|
f->sound_random = "mob-kitty-env";
|
|
f->spawn_min_height = -5;
|
|
f->spawn_max_height = 40;
|
|
f->attack_player_damage = 1;
|
|
f->attack_player_range = v3f(1,1,1);
|
|
f->setCollisionBox(aabb3f(-0.6*BS, 0., -0.6*BS, 0.6*BS, 1.25*BS, 0.6*BS));
|
|
content_list_add("creative",CONTENT_TOOLITEM_MOB_SPAWNER,1,i);
|
|
}
|