SERVER: Major Mystery Box Revamp, add support for MBOX2 format

This commit is contained in:
Ian 2023-08-29 11:44:41 -04:00
parent 5d392899bb
commit ea90417314
8 changed files with 504 additions and 272 deletions

View file

@ -945,11 +945,11 @@ void(float width, float height) HUD_Useprint =
usestring = strcat("Hold ",usespace, " to Rebuild Barrier");
break;
case 6://box
usestring = strcat("Hold ",usespace, " to buy a Random Weapon");
usestring = strcat("Hold ",usespace, " for Mystery Box");
usecost = strcat("[Cost:", ftos(useprint_cost), "]");
break;
case 7://box take
usestring = strcat("Hold ",usespace, " to take Weapon");
usestring = strcat("Hold ",usespace, " for ", GetWeaponName(useprint_weapon));
break;
case 8://power
usestring = "The Power must be Activated first";

View file

@ -3,7 +3,7 @@
put custom server-only globals and fields here
Copyright (C) 2021-2022 NZ:P Team
Copyright (C) 2021-2023 NZ:P Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@ -66,7 +66,6 @@ void(string com) SV_ParseClientCommand;
float ach_tracker_col2;
float ach_tracker_barr;
float ach_tracker_spin;
float ach_tracker_luck;
float global_trace_damage_multiplier;
@ -396,14 +395,30 @@ float isPowerOn;
.float revivesoda;
.float collected;
.float boxstatus;
// Mystery Box
#define MAX_BOX_WEAPONS 27
.float boxstatus;
.entity boxweapon;
.float spins;
.float papState;
float BoxWeapons[27];
float mystery_box_count;
entity mystery_boxes[16];
vector boxOrigin;
.float spins;
.float papState;
var struct mbox_struct
{
float weapon_id; // ID for the relevant weapon.
float allowed; // 1 for allowed, 0 for denied.
float rarity; // 0-100 (float) percent change for obtaining. -1 for normal.
} mystery_box_weapons[MAX_BOX_WEAPONS] = {};
float mystery_box_count;
float mystery_box_leave_count;
float mystery_box_cost;
string mystery_box_model;
string mystery_box_glow_model;
string mystery_box_open_sound;
string mystery_box_close_sound;
entity mystery_boxes[16];
vector mystery_box_start_origin;
#ifdef FTE

View file

@ -109,7 +109,6 @@ void() light_flame_small_white = // Light with small flame & fire sound
//
void(entity e) Light_Red =
{
e.effects = e.effects | EF_FULLBRIGHT;
#ifndef FTE
@ -135,7 +134,6 @@ void(entity e) Light_Red =
//
void(entity e) Light_Green =
{
e.effects = e.effects | EF_FULLBRIGHT;
#ifndef FTE
@ -161,7 +159,6 @@ void(entity e) Light_Green =
//
void(entity e) Light_Blue =
{
e.effects = e.effects | EF_FULLBRIGHT;
#ifndef FTE
@ -187,7 +184,6 @@ void(entity e) Light_Blue =
//
void(entity e) Light_Orange =
{
e.effects = e.effects | EF_FULLBRIGHT;
#ifndef FTE
@ -213,7 +209,6 @@ void(entity e) Light_Orange =
//
void(entity e) Light_Purple =
{
e.effects = e.effects | EF_FULLBRIGHT;
#ifndef FTE
@ -239,7 +234,6 @@ void(entity e) Light_Purple =
//
void(entity e) Light_Cyan =
{
e.effects = e.effects | EF_FULLBRIGHT;
#ifndef FTE
@ -265,7 +259,6 @@ void(entity e) Light_Cyan =
//
void(entity e) Light_Pink =
{
e.effects = e.effects | EF_FULLBRIGHT;
#ifndef FTE
@ -291,7 +284,6 @@ void(entity e) Light_Pink =
//
void(entity e) Light_Lime =
{
e.effects = e.effects | EF_FULLBRIGHT;
#ifndef FTE
@ -317,7 +309,6 @@ void(entity e) Light_Lime =
//
void(entity e) Light_Yellow =
{
e.effects = e.effects | EF_FULLBRIGHT;
#ifndef FTE

View file

@ -29,7 +29,6 @@ float backupWepSkin;
float sound_perk_delay;
void() W_Switch;
void() W_TakeOut;
void() mystery_touch;
//
// --------------------

View file

@ -26,10 +26,55 @@
*/
void(vector where, float time_alive) SpawnSpark;
void() MBOX_Touch;
#define MBOX_SPAWNFLAG_NOTHERE 1
#define MBOX_SPAWNFLAG_NOLIGHT 2
//
// MBOX_UpdateGlowFrame()
// Updates the Glow model frame to match
// the box if it exists.
//
void() MBOX_UpdateGlowFrame =
{
if (self.goaldummy) self.goaldummy.frame = self.frame;
};
//
// MBOX_PlayOpenAnimation()
// Plays the open animation for the Mystery Box,
// frames 1-8.
//
void() MBOX_PlayOpenAnimation = [1, MBOX_OpenAnimation2 ] { self.frame = 1; MBOX_UpdateGlowFrame(); Light_Yellow(self); };
void() MBOX_OpenAnimation2 = [2, MBOX_OpenAnimation3 ] { self.frame = 2; MBOX_UpdateGlowFrame(); };
void() MBOX_OpenAnimation3 = [3, MBOX_OpenAnimation4 ] { self.frame = 3; MBOX_UpdateGlowFrame(); };
void() MBOX_OpenAnimation4 = [4, MBOX_OpenAnimation5 ] { self.frame = 4; MBOX_UpdateGlowFrame(); };
void() MBOX_OpenAnimation5 = [5, MBOX_OpenAnimation6 ] { self.frame = 5; MBOX_UpdateGlowFrame(); };
void() MBOX_OpenAnimation6 = [6, MBOX_OpenAnimation7 ] { self.frame = 6; MBOX_UpdateGlowFrame(); };
void() MBOX_OpenAnimation7 = [7, MBOX_OpenAnimation8 ] { self.frame = 7; MBOX_UpdateGlowFrame(); };
void() MBOX_OpenAnimation8 = [8, SUB_Null ] { self.frame = 8; MBOX_UpdateGlowFrame(); };
//
// MBOX_Reset()
// Resets the Mystery Box to be ready for
// another use.
//
inline void() MBOX_Reset =
{
self.frame = 0;
self.boxstatus = 0;
};
//
// MBOX_PlayCloseAnimation()
// Plays the open animation for the Mystery Box,
// frames 9-12.
//
void() MBOX_PlayCloseAnimation = [9, MBOX_CloseAnimation2 ] { self.frame = 9; MBOX_UpdateGlowFrame(); sound (self, CHAN_ITEM, mystery_box_close_sound, 1, ATTN_NORM); Light_None(self); };
void() MBOX_CloseAnimation2 = [10, MBOX_CloseAnimation3 ] { self.frame = 10; MBOX_UpdateGlowFrame(); };
void() MBOX_CloseAnimation3 = [10, MBOX_CloseAnimation4 ] { self.frame = 11; MBOX_UpdateGlowFrame(); };
void() MBOX_CloseAnimation4 = [10, MBOX_Reset ] { self.frame = 12; MBOX_UpdateGlowFrame(); };
//
// MBOX_FreeEnt(ent)
@ -66,137 +111,21 @@ entity() MBOX_GetFreeEnt =
return ent;
};
void() updateBoxGlow
//
// MBOX_GetRandomBoxWeapon(user)
// Returns a weapon from the Mystery Box allow-list
// that the user is not holding.
//
float(entity user) MBOX_GetRandomBoxWeapon =
{
if(self.goaldummy)
{
self.goaldummy.frame = self.frame;
}
};
float weapon_index = rint((random() * (MAX_BOX_WEAPONS - 1)));
float weapon_id = mystery_box_weapons[weapon_index].weapon_id;
float weapon_allowed = mystery_box_weapons[weapon_index].allowed;
void() box_open1 =[ 1, box_open2 ] { self.frame = 1;updateBoxGlow(); Light_Yellow(self);};
void() box_open2 =[ 2, box_open3 ] { self.frame = 2;updateBoxGlow();};
void() box_open3 =[ 3, box_open4 ] { self.frame = 3;updateBoxGlow();};
void() box_open4 =[ 4, box_open5 ] { self.frame = 4;updateBoxGlow();};
void() box_open5 =[ 5, box_open6 ] { self.frame = 5;updateBoxGlow();};
void() box_open6 =[ 6, box_open7 ] { self.frame = 6;updateBoxGlow();};
void() box_open7 =[ 7, box_open8 ] { self.frame = 7;updateBoxGlow();};
void() box_open8 =[ 8, SUB_Null ] { self.frame = 8;updateBoxGlow();};
void() resetbox =
{
self.frame = 0;
self.boxstatus = 0;
};
void() box_close1 =[ 9, box_close2 ] { self.frame = 9;updateBoxGlow();sound (self, CHAN_ITEM, "sounds/machines/mbox_close.wav", 1, ATTN_NORM); Light_None(self);};
void() box_close2 =[ 10, box_close3 ] { self.frame = 10;updateBoxGlow();};
void() box_close3 =[ 11, box_close4 ] { self.frame = 11;updateBoxGlow();};
void() box_close4 =[ 12, resetbox ] { self.frame = 12;updateBoxGlow();};
//
// Getweaponid()
// Returns weapon ID from Mbox ID, as they are slightly different
// because.. reasons..
//
float(float r) Getweaponid =
{
switch(r) {
case 0:
return W_COLT;
case 1:
return W_KAR;
case 2:
return W_DB;
case 3:
return W_MG;
case 4:
return W_RAY;
case 5:
return W_THOMPSON;
case 6:
return W_M2;
case 7:
return W_PPSH;
case 8:
return W_SAWNOFF;
case 9:
return W_TESLA;
case 10:
return W_M1A1;
case 11:
return W_GEWEHR;
case 12:
return W_FG;
case 13:
return W_BROWNING;
case 14:
return W_KAR_SCOPE;
case 15:
return W_357;
case 16:
return W_STG;
case 17:
return W_PANZER;
case 18:
return W_BK;
case 19:
return W_PTRS;
case 20:
return W_MP40;
case 21:
return W_TRENCH;
case 22:
return W_BAR;
case 23:
return W_M1;
case 24:
return W_TYPE;
case 25:
return W_MP5K;
case 26:
return W_SPRING;
}
return r;
}
//
// randomweapon()
// Returns a Weapon if permitted by the map's MBOX data file.
//
float() randomweapon =
{
float r;
r = rint((random() * 26));
// If this weapon is in our Box Array, we can return it.
if (BoxWeapons[r] == 1) {
return r;
} else { // It's not in the Array, try again until we find one.
return randomweapon();
}
};
//
// CheckWeapon(w, user)
// Checks all 3 weapon slots to see if the Player is holding specified
// Weapon, ensures we do not give Duplicates.
//
float CheckWeapon (float w, entity user) =
{
// Non-PaP Weapons
if (user.weapon == w || user.secondaryweapon == w || user.thirdweapon == w)
return 0;
// PaP Weapons
if (EqualNonPapWeapon(user.weapon) == w || EqualNonPapWeapon(user.secondaryweapon) == w || EqualNonPapWeapon(user.thirdweapon) == w)
return 0;
// We passed both, this weapon is okay
return 1;
if (weapon_allowed == true && !Weapon_PlayerHasWeapon(user, weapon_id, true))
return weapon_id;
else
return MBOX_GetRandomBoxWeapon(user);
};
//
@ -209,7 +138,7 @@ void() Reset_MBox =
self.velocity = '0 0 0';
tempe = self;
self = self.owner;
box_close1();
MBOX_PlayCloseAnimation();
self = tempe;
self.owner.owner = world;
self.owner.boxstatus = 0;
@ -264,7 +193,7 @@ void() findboxspot =
g = MBOX_GetFreeEnt();
g.classname = "mystery_glow";
newspot.goaldummy = g;
setmodel(g,"models/machines/mglow$.mdl");
setmodel(g, mystery_box_glow_model);
setorigin(g,newspot.origin);
g.angles = newspot.angles;
@ -282,23 +211,23 @@ void() findboxspot =
// Set some values and change the found Spot to an MBox
newspot.spins = 0;
newspot.boxstatus = 0;
newspot.touch = mystery_touch;
newspot.touch = MBOX_Touch;
newspot.solid=SOLID_TRIGGER;
newspot.classname = "mystery";
newspot.spawnflags = self.owner.spawnflags;
setorigin(newspot, newspot.origin);
setmodel (newspot, "models/machines/mystery.mdl");
setmodel (newspot, mystery_box_model);
newspot.frame = 0;
setsize (newspot, VEC_HULL2_MIN, VEC_HULL2_MAX);
}
//
// MBOX_MakeActive()
// Sets the Mystery Box touch to mystery_touch (delay)
// Sets the Mystery Box touch to MBOX_Touch (delay)
//
void() MBOX_MakeActive =
{
self.touch = mystery_touch;
self.touch = MBOX_Touch;
}
//
@ -326,7 +255,7 @@ void() MBOX_FindNewSpot =
new_box.solid = SOLID_TRIGGER;
new_box.classname = "mystery";
new_box.frame = 0;
setmodel(new_box, "models/machines/mystery.mdl");
setmodel(new_box, mystery_box_model);
setsize(new_box, VEC_HULL2_MIN, VEC_HULL2_MAX);
// Spawn the Box Glow if permitted
@ -336,7 +265,7 @@ void() MBOX_FindNewSpot =
light = MBOX_GetFreeEnt();
light.classname = "mystery_glow";
new_box.goaldummy = light;
setmodel(light,"models/machines/mglow$.mdl");
setmodel(light, mystery_box_glow_model);
setorigin(light, new_box.origin);
light.angles = new_box.angles;
light.effects = EF_FULLBRIGHT;
@ -481,7 +410,7 @@ void() MBOX_TeddyLeave =
void() MBOX_PresentTeddy =
{
// Return the Player's points.
addmoney(self.owner.owner, 950, 0);
addmoney(self.owner.owner, mystery_box_cost, 0);
// Broadcast the bad luck.
sound(self, CHAN_ITEM, "sounds/misc/buy.wav", 1, ATTN_NONE);
sound(self, 2, "sounds/misc/giggle.wav", 1, ATTN_NONE);
@ -490,54 +419,79 @@ void() MBOX_PresentTeddy =
self.nextthink = time + 2;
}
//
// MBOX_GetLeaveChancePercentage()
// Generates a percentage chance for the Mystery Box
// to leave.
//
float(float uses) MBOX_GetLeaveChancePercentage =
{
// If there's only one Mystery Box, never try to leave.
// Additionally, you're always given 4 free uses.
if (mystery_box_count == 1 || uses <= 4)
return 0;
float chance_to_leave = 0;
// There is a hardcoded 100% chance to leave after 8 uses
// if the box hasn't moved yet in a game.
if (mystery_box_leave_count == 0 && uses >= 8)
chance_to_leave = 100;
// There's a 15% chance for leaving if this is between the uses
// of 4 and 8.
if (uses >= 4 && uses < 8)
chance_to_leave = 15;
// Mystery Box behavior changes after it leaves for the first time
if (mystery_box_leave_count > 0) {
// 30% chance of leaving between uses 8 and 12.
if (uses >= 8 && uses < 12)
chance_to_leave = 30;
// 50% chance after the 12th use.
else if (uses >= 12)
chance_to_leave = 50;
}
return chance_to_leave/100;
}
void() Float_Change =
{
entity tpspot;
float r, tempf, teddygen;
float tempf;
string temps;
tempf = randomweapon();
r = Getweaponid(tempf);
while (!CheckWeapon (r, self.owner.owner))
{
tempf = randomweapon();
r = Getweaponid(tempf);
}
temps = GetWeaponModel(r, 1);
float leave_chance;
tempf = MBOX_GetRandomBoxWeapon(self.owner.owner);
temps = GetWeaponModel(tempf, 1);
setmodel (self, temps);
self.boxstatus = self.boxstatus + 0.01;
if (self.wait <= time)
{
tpspot = find(world, classname, "mystery_box_tp_spot");
if (tpspot != world && self.owner.spins > 3) {
teddygen = random();
//bprint(PRINT_HIGH, "found tp spot\n");
} else {
teddygen = 0;
//bprint(PRINT_HIGH, "no tp spot or spins < 3\n");
}
leave_chance = MBOX_GetLeaveChancePercentage(self.owner.spins);
self.velocity = '0 0 0';
if (!teddygen || teddygen < 0.7) { //teddy gen threshold, high means less chance
if (random() > leave_chance) { //teddy gen threshold, high means less chance
self.owner.boxstatus = 2;
self.weapon = r;
self.weapon = tempf;
self.nextthink = time + 5;
self.think = Float_Decrease;
//bprint(PRINT_HIGH, "spot not found, or teddygun is > 0.7\n");
return;
}
else {
addmoney(self.owner.owner, 950, 0);
addmoney(self.owner.owner, mystery_box_cost, 0);
self.model = "models/props/teddy.mdl";
setmodel(self, self.model);
self.angles_y = self.angles_y - 90;
self.nextthink = time + 1;
self.think = MBOX_PresentTeddy;
ach_tracker_luck++;
mystery_box_leave_count++;
if (ach_tracker_luck >= 10)
if (mystery_box_leave_count >= 10)
GiveAchievement(11);
return;
}
@ -566,7 +520,7 @@ void() finish_mbox_setup =
g.classname = "mystery_glow";
self.goaldummy = g;
setmodel(g,"models/machines/mglow$.mdl");
setmodel(g, mystery_box_glow_model);
setorigin(g,self.origin);
g.angles = self.angles;
@ -579,34 +533,32 @@ void() finish_mbox_setup =
}
}
void() allocate_floating_weapons =
//
// MBOX_AllocateTempEntities()
// Spawns in all of the temp entities that are
// used for the floating weapons, teddy bear,
// and glow.
//
void() MBOX_AllocateTempEntities =
{
self.think = SUB_Null;
// Spawn all of our floating weapon entities
// Multiply by 2 to account for the glow.
for (float i = 0; i < mystery_box_count * 3; i++) {
entity tempe = spawn();
tempe.classname = "freeMboxEntity";
}
finish_mbox_setup();
}
};
void() Create_Floating_Weapon =
{
entity gun;
float r, tempf;
float tempf;
string temps;
tempf = randomweapon();
r = Getweaponid(tempf);
while (!CheckWeapon (r, self.owner.owner))
{
tempf = randomweapon();
r = Getweaponid(tempf);
}
temps = GetWeaponModel(r, 1);
tempf = MBOX_GetRandomBoxWeapon(self.owner.owner);
temps = GetWeaponModel(tempf, 1);
gun = MBOX_GetFreeEnt();
gun.classname = "mystery_weapon";
@ -615,7 +567,6 @@ void() Create_Floating_Weapon =
setmodel (gun, temps);
setsize (gun, '0 0 0', '0 0 0');
gun.angles = self.angles;
gun.effects = EF_FULLBRIGHT;
gun.movetype = MOVETYPE_NOCLIP;
gun.solid = SOLID_NOT;
@ -653,7 +604,7 @@ void() mystery_box_tp_spot =
mystery_box_count++;
};
void() mystery_touch =
void() MBOX_Touch =
{
entity tempe;
@ -661,10 +612,17 @@ void() mystery_touch =
return;
if (!self.boxstatus) {
useprint (other, 6, 950, 0);
useprint (other, 6, mystery_box_cost, 0);
}
if (self.boxstatus == 2 && self.owner == other) {
useprint (other, 7, 0, 0);
#ifndef FTE
other.Weapon_Name_Touch = GetWeaponName(self.boxweapon.weapon);
#endif // FTE
useprint (other, 7, 0, self.boxweapon.weapon);
}
if (other.button7 && !other.semiuse)
@ -672,13 +630,13 @@ void() mystery_touch =
other.semiuse = true;
if (!self.boxstatus)
{
if (other.points >= 950)
if (other.points >= mystery_box_cost)
{
sound (self, CHAN_ITEM, "sounds/machines/mbox_open.wav", 1, ATTN_NORM);
addmoney(other, -950, FALSE);
sound (self, CHAN_ITEM, mystery_box_open_sound, 1, ATTN_NORM);
addmoney(other, -mystery_box_cost, FALSE);
self.boxstatus = 1;
self.owner = other;
box_open1 ();
MBOX_PlayOpenAnimation();
Create_Floating_Weapon();
self.spins++;
}
@ -781,91 +739,314 @@ void() mystery_touch =
SwitchWeapon(self.weapon);
self = tempe;
MBOX_FreeEnt(self.boxweapon);
box_close1();
MBOX_PlayCloseAnimation();
}
}
}
}
//
// Load_Mbox_Data()
// Opens the map's MBOX Data File and adds parsed data
// into the Box's weapon Array.
// ----------
// TODO: Possibly investigate making this a little better and more modular
// so adding MBOX Weapons can be easier?
// MBOX_GetWeaponIDFromMB1(mbox_id)
// .mbox weapon IDs did not 100% correlate
// to internal weapon IDs, so this converts
// them appropriately.
//
void() Load_Mbox_Data =
float MBOX_GetWeaponIDFromMB1(float mbox_id) =
{
float file;
string h;
int weapons_all_disabled = 1;
switch(mbox_id) {
case 0: return W_COLT;
case 1: return W_KAR;
case 2: return W_DB;
case 3: return W_MG;
case 4: return W_RAY;
case 5: return W_THOMPSON;
case 6: return W_M2;
case 7: return W_PPSH;
case 8: return W_SAWNOFF;
case 9: return W_TESLA;
case 10: return W_M1A1;
case 11: return W_GEWEHR;
case 12: return W_FG;
case 13: return W_BROWNING;
case 14: return W_KAR_SCOPE;
case 15: return W_357;
case 16: return W_STG;
case 17: return W_PANZER;
case 18: return W_BK;
case 19: return W_PTRS;
case 20: return W_MP40;
case 21: return W_TRENCH;
case 22: return W_BAR;
case 23: return W_M1;
case 24: return W_TYPE;
case 25: return W_MP5K;
case 26: return W_SPRING;
default: return W_COLT;
}
return W_COLT;
}
// Attempt to Open the File
h = strcat(mapname, ".mbox");
h = strcat("maps/", h);
file = fopen (h, FILE_READ);
//
// MBOX_PrecacheWeaponData()
// Iterates through the weapon array and
// allocates necessary content for each
// allowed weapon.
//
void() MBOX_PrecacheWeaponContent =
{
for (float i = 0; i < MAX_BOX_WEAPONS; i++) {
if (mystery_box_weapons[i].allowed == true) {
precache_model(GetWeaponModel(mystery_box_weapons[i].weapon_id, 0));
precache_model(GetWeaponModel(mystery_box_weapons[i].weapon_id, 1));
precache_extra(mystery_box_weapons[i].weapon_id);
}
}
}
// There was no MBOX Data, enable all Weapons!
// R.I.P. PSP Memory 90% of the time in this case..
if (file == -1) {
for (float i = 0; i < 27; i++) {
BoxWeapons[i] = 1;
}
fclose(file);
} else {
// Parse each Line and write the Data into our Array.
for (float i = 0; i < 27; i++) {
h = strtrim((fgets(file)));
BoxWeapons[i] = stof(h);
if (stof(h) == 1)
weapons_all_disabled = 0;
}
}
if (weapons_all_disabled) {
for (float i = 0; i < 27; i++) {
BoxWeapons[i] = 1;
}
}
//
// MBOX_ParseMB1File(mbox_file)
// Parses the old (2014) .mbox file and loads
// it into the necessary structure.
//
void(float mbox_file) MBOX_ParseMB1File =
{
string file_line;
for(float i = 0; i < 27; i++) {
// Precache Weapon Data if enabled
if (BoxWeapons[i]) {
precache_model(GetWeaponModel(Getweaponid(i), 0));
precache_model(GetWeaponModel(Getweaponid(i), 1));
precache_extra(Getweaponid(i));
}
}
// Parse the .mbox line-by-line and fill the data structure.
for (float i = 0; i < MAX_BOX_WEAPONS; i++) {
file_line = strtrim((fgets(mbox_file)));
mystery_box_weapons[i].weapon_id = MBOX_GetWeaponIDFromMB1(i);
mystery_box_weapons[i].allowed = stof(file_line);
mystery_box_weapons[i].rarity = -1;
}
fclose(file);
// Precache the allowed weapons
MBOX_PrecacheWeaponContent();
}
//
// MBOX_GetUnusedWeaponID()
// Returns a weapon ID for a potential
// box weapon that is not currently
// in the list.
//
float() MBOX_GetUnusedWeaponID =
{
// Iterate over every potential Box Weapon
for (float i = 0; i < MAX_BOX_WEAPONS; i++) {
float weapon_in_use = false;
float weapon_id = MBOX_GetWeaponIDFromMB1(i);
// Now over every weapon in the list
for (float j = 0; j < MAX_BOX_WEAPONS; j++) {
if (mystery_box_weapons[j].weapon_id == weapon_id)
weapon_in_use = true;
}
if (weapon_in_use == false) {
return weapon_id;
}
}
error("MBOX_GetunusedWeaponID: Could not find a free weapon slot!\n");
return 0;
}
//
// MBOX_ParseMB2File(mbox_file)
// Parses the current .mb2 file and loads it
// into the necessary structure.
//
void(float mbox_file) MBOX_ParseMB2File =
{
string file_line;
// Read the first line: It's our "header", should be
// "nzp_mbox2".
file_line = strtrim((fgets(mbox_file)));
if (file_line != "nzp_mbox2")
error(strcat("MBOX_ParseMB2File: Expected \"nzp_mbox2\" but got ", file_line));
// Read each line of the file in a loop
float finished_parsing = false;
float parsing_state = 0;
float is_allow_not_deny = false;
float weapons_parsed = 0;
float weapon_id = 0;
while(!finished_parsing) {
// Grab a new line
file_line = fgets(mbox_file);
// End of file.
if not (file_line) {
finished_parsing = true;
break;
}
file_line = strzone(strtrim(file_line));
// Check for comments, they always start with '#'.
// Also ignore whitespace.
string first_char = strzone(substring(file_line, 0, 1));
if (first_char == "#" || file_line == "") {
continue;
}
strunzone(first_char);
//
// Actual Content Parsing
//
// Retrieving if its an allow or deny list.
if (parsing_state == 0) {
if (file_line == "allow:")
is_allow_not_deny = true;
else if (file_line == "deny:")
is_allow_not_deny = false;
else
error(strcat("MBOX_ParseMB2File: Not an allow/deny specifier - ", file_line));
// Now that we know the list, we can begin
// parsing the weapons inside of it.
parsing_state++;
} else if (parsing_state == 1) {
// Get the weapon ID from it's name.
weapon_id = WepDef_GetWeaponIDFromName(file_line);
if (weapon_id == W_NOWEP)
error(strcat("MBOX_ParseMB2File: Not a weapon - ", file_line));
// Fill the list with it.
mystery_box_weapons[weapons_parsed].weapon_id = weapon_id;
mystery_box_weapons[weapons_parsed].allowed = is_allow_not_deny;
weapons_parsed++;
}
strunzone(file_line);
}
// At this point, the file has been parsed with all of the
// allowed/denied weapons, now we have to automatically
// fill in the rest for the opposite list.
for (float i = weapons_parsed; i < MAX_BOX_WEAPONS; i++) {
// We need to pick an ID to assign that hasn't already
// been used.
weapon_id = MBOX_GetUnusedWeaponID();
mystery_box_weapons[i].weapon_id = weapon_id;
mystery_box_weapons[i].allowed = !is_allow_not_deny;
}
// Precache the allowed weapons
MBOX_PrecacheWeaponContent();
}
//
// MBOX_LoadData()
// Seeks for either an .mb2 or .mbox file,
// and adds the contents into the Mystery Box
// weapon list.
//
void() MBOX_LoadData =
{
float mbox_file;
string file_path;
// Attempt 1: Seek for maps/mapname.mb2 (mbox-2)
file_path = strcat(mapname, ".mb2");
file_path = strcat("maps/", file_path);
mbox_file = fopen(file_path, FILE_READ);
if (mbox_file != -1) {
MBOX_ParseMB2File(mbox_file);
fclose(mbox_file);
return;
}
// Attempt 2: Seek for maps/mapname.mbox (mbox-1)
file_path = strcat(mapname, ".mbox");
file_path = strcat("maps/", file_path);
mbox_file = fopen(file_path, FILE_READ);
if (mbox_file != -1) {
MBOX_ParseMB1File(mbox_file);
fclose(mbox_file);
return;
}
// Attempt 3: Include All Weapons
for (float i = 0; i < MAX_BOX_WEAPONS; i++) {
mystery_box_weapons[i].weapon_id = MBOX_GetWeaponIDFromMB1(i);
mystery_box_weapons[i].allowed = true;
mystery_box_weapons[i].rarity = -1;
}
// Precache it all.
MBOX_PrecacheWeaponContent();
// Close the file pointer just in case.
fclose(mbox_file);
}
void() mystery_box =
{
Load_Mbox_Data();
// Load and cache all of the allowed weapons into the hunk.
MBOX_LoadData();
precache_model ("models/machines/mystery.mdl");
precache_sound ("sounds/machines/mbox_open.wav");
precache_sound ("sounds/machines/mbox_close.wav");
//
// Set Default Stats for Compatibility
//
// Model
if (!self.model) {
self.model = "models/machines/mystery.mdl";
}
if (!(self.spawnflags & MBOX_SPAWNFLAG_NOLIGHT))
precache_model ("models/machines/mglow$.mdl");
// Light Model
if (!self.weapon2model) {
self.weapon2model = "models/machines/mglow$.mdl";
}
// Cost
if (!self.cost) {
self.cost = 950;
}
// Open Sound
if (!self.oldmodel) {
self.oldmodel = "sounds/machines/mbox_open.wav";
}
// Close Sound
if (!self.powerup_vo) {
self.powerup_vo = "sounds/machines/mbox_close.wav";
}
// Store the custom presentation as globals
mystery_box_model = self.model;
mystery_box_glow_model = self.weapon2model;
mystery_box_open_sound = self.oldmodel;
mystery_box_close_sound = self.powerup_vo;
mystery_box_cost = self.cost;
precache_model(mystery_box_model);
if (!(self.spawnflags & MBOX_SPAWNFLAG_NOLIGHT))
precache_model (mystery_box_glow_model);
precache_sound(mystery_box_open_sound);
precache_sound(mystery_box_close_sound);
self.solid = SOLID_TRIGGER;
self.classname = "mystery";
setorigin(self, self.origin);
setmodel (self, "models/machines/mystery.mdl");
setmodel (self, mystery_box_model);
setsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);
self.touch = mystery_touch;
boxOrigin = self.origin;
self.touch = MBOX_Touch;
mystery_box_start_origin = self.origin;
mystery_boxes[mystery_box_count] = self;
mystery_box_count++;
self.think = allocate_floating_weapons;
self.think = MBOX_AllocateTempEntities;
self.nextthink = time + 0.2;
}
};

View file

@ -62,6 +62,7 @@ inline void(entity ent) PU_FreeEnt =
ent.touch = SUB_Null;
ent.think = SUB_Null;
ent.frame = 0;
ent.effects = 0;
};
//
@ -665,6 +666,7 @@ void(vector where, float type) Spawn_Powerup =
}
// Assign the Power-Up model and sound
powerup.effects = EF_FULLBRIGHT;
powerup.model = powerup.oldmodel = PU_ModelPath(powerup.walktype);
setmodel(powerup, powerup.model);
powerup.powerup_vo = PU_VoiceoverPath(powerup.walktype);

View file

@ -34,7 +34,7 @@ void() setup_perk;
void() touch_perk;
void(entity ent) MBOX_FreeEnt;
entity() MBOX_GetFreeEnt;
void() mystery_touch;
void() MBOX_Touch;
#define MBOX_SPAWNFLAG_NOLIGHT 2
@ -185,7 +185,7 @@ void() GameRestart_ResetMysteryBox =
// If the Mystery Box is not in its original
// location.
if (mystery_box.origin != boxOrigin) {
if (mystery_box.origin != mystery_box_start_origin) {
mystery_box.model = "models/props/teddy.mdl";
mystery_box.frame = 2;
mystery_box.classname = "mystery_box_tp_spot";
@ -196,11 +196,11 @@ void() GameRestart_ResetMysteryBox =
MBOX_FreeEnt(mystery_box.goaldummy);
// This isn't the normal spawn position
if (mystery_box.origin != boxOrigin) {
if (mystery_box.origin != mystery_box_start_origin) {
// Find the original spot
entity original_box = find(world, classname, "mystery_box_tp_spot");
while (original_box != world) {
if (original_box.origin == boxOrigin)
if (original_box.origin == mystery_box_start_origin)
break;
original_box = find(original_box, classname, "mystery_box_tp_spot");
@ -212,20 +212,23 @@ void() GameRestart_ResetMysteryBox =
light.classname = "mystery_glow";
original_box.goaldummy = light;
setmodel(light, "models/machines/mglow$.mdl");
setmodel(light, mystery_box_glow_model);
setorigin(light, original_box.origin);
light.angles = original_box.angles;
#ifdef FTE
light.alpha = 0.5;
#endif
#endif // FTE
}
original_box.touch = mystery_touch;
original_box.touch = MBOX_Touch;
original_box.solid = SOLID_TRIGGER;
original_box.classname = "mystery";
setorigin(original_box, original_box.origin);
setmodel(original_box, "models/machines/mystery.mdl");
setmodel(original_box, mystery_box_model);
setsize (original_box, VEC_HULL2_MIN, VEC_HULL2_MAX);
}

View file

@ -3,7 +3,7 @@
all weapon stats are stored here
Copyright (C) 2021-2022 NZ:P Team
Copyright (C) 2021-2023 NZ:P Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@ -4240,3 +4240,44 @@ float(float wep) GetWeaponZoomAmount =
return 0;
}
//
// WepDef_GetWeaponIDFromName(weapon)
// Takes a string input and returns the weapon ID
// from the associated string. Will be deprecated
// when weapons are data-driven.
//
float(string weapon) WepDef_GetWeaponIDFromName =
{
switch(weapon) {
case "m1911": return W_COLT;
case "kar98k": return W_KAR;
case "thompson": return W_THOMPSON;
case "357_magnum": return W_357;
case "bar": return W_BAR;
case "ballistic_knife": return W_BK;
case "browning": return W_BROWNING;
case "double_barreled_shotgun": return W_DB;
case "fg42": return W_FG;
case "gewehr": return W_GEWEHR;
case "kar98k_scoped": return W_KAR_SCOPE;
case "m1_garand": return W_M1;
case "m1a1_carbine": return W_M1A1;
case "m2_flamethrower": return W_M2;
case "mp40": return W_MP40;
case "mg42": return W_MG;
case "panzerschreck": return W_PANZER;
case "ppsh-41": return W_PPSH;
case "ptrs-41": return W_PTRS;
case "ray_gun": return W_RAY;
case "sawed_off_shotgun": return W_SAWNOFF;
case "stg-44": return W_STG;
case "trenchgun": return W_TRENCH;
case "type_100": return W_TYPE;
case "wunderwaffe": return W_TESLA;
case "mp5k": return W_MP5K;
case "springfield": return W_SPRING;
default: return W_NOWEP;
}
return W_COLT;
}