Morrowing

Ein neuer Monat, ein neuer Morrowing Teil für Sie. Dieses Mal sind ein Inventar und ein Blitzschlagzauber hinzugekommen, um die wichtigsten Neuerungen zu nennen. Ich habe einige Models und Grafiken meines Inventars aus AUM 18 übernommen, aber der Code ist komplett anders mit mehr Features.
panel
main_up_pan // the upper part of the main panel
{
bmap = main_up_tga;
pos_x = 945;
pos_y = 100;
layer = 10;
flags = overlay, refresh, visible;
}
panel main_slot1_pan // the first inventory slot
{
bmap = main_slot1_tga;
pos_x = 945;
pos_y = 140;
layer = 10;
on_click = place_item1;
flags = overlay, refresh, visible;
}
panel main_slot2_pan // the second inventory slot
{
bmap = main_slot2_tga;
pos_x = 945;
pos_y = 248;
layer = 10;
on_click = place_item2;
flags = overlay, refresh, visible;
}
panel main_slot3_pan // the third inventory slot
{
bmap = main_slot3_tga;
pos_x = 945;
pos_y = 357;
layer = 10;
on_click = place_item3;
flags = overlay, refresh, visible;
}
panel main_slot4_pan // the fourth inventory slot
{
bmap = main_slot4_tga;
pos_x = 945;
pos_y = 464;
layer = 10;
on_click = place_item4;
flags = overlay, refresh, visible;
}
panel main_down_pan // the lower part of the main panel
{
bmap = main_down_tga;
pos_x = 945;
pos_y = 567;
layer = 10;
window = 3, 2, 74, 7, red_pcx, player.strength, 0; // can range
from 0 to 74 (the cutout window bitmap has 74x7 pixels)
window = 3, 14, 74, 7, green_pcx, player.experience, 0; // can
range from 0 to 74
window = 3, 26, 74, 7, blue_pcx, player.mana, 0; // can range from
0 to 74
flags = overlay, refresh, visible;
}

Das Inventarpanel besteht aus 6 kleinen Panels, die nahtlos aneinanderpassen. Ich habe mich für diese Variante entschieden, weil so jeder Slot ein separates Panel mit eigener “on_click” Funktion ist.
panel shield_pan // displays the shield on the panel
{
bmap = shield_pcx;
layer = 11;
flags = overlay, refresh;
on_click = use_shield;
}
panel mace_pan // displays the mace on the panel
{
bmap = mace_pcx;
layer = 11;
flags = overlay, refresh;
on_click = use_mace;
}
panel armor_pan // displays the armor on the panel
{
bmap = armor_pcx;
layer = 11;
flags = overlay, refresh;
on_click = use_armor;
}
panel flare_pan // displays the flare on the panel
{
bmap = flare_pcx;
layer = 11;
flags = overlay, refresh;
on_click = use_flare;
}
Diese Defintionen sind für die Bilder der Items (die Rüstung und das Schild im Bild sind zwei Beispiele).
starter init_player()
{
while (player == null) {wait (1);}
player.strength = 10;
player.experience = 0;
player.mana = 0;
while (1)
{
player.mana += 0.1 * time; // increase the mana slowly
player.mana = min(74, player.mana); // limit player.mana
to 74
wait (1);
}
}
Diese Starterfunktion initialisiert den Spieler und setzt Stärke, Erfahrung (diese steigt durch das Töten von Skeletten) und Mana fest. Der Spieler braucht 70 Manapunkte für seinen Blitzschlag; wie Sie sehen regeneriert das Mana sich langsam und ist auf 74 begrenzt (die Breite des Bitmaps auf dem Display).
function place_item1()
{
// don't do anything if the player hasn't picked up an item yet or if
this slot
is already full
if ((mouse_map == pointer_pcx) || (item1_placed == 1)) {return;}
if (mouse_map == shield_pcx) // picked up the shield
{
item_ptr = shield_pan;
}
if (mouse_map == mace_pcx) // picked up the shield
{
item_ptr = mace_pan;
}
if (mouse_map == armor_pcx) // picked up the armor
{
item_ptr = armor_pan;
}
if (mouse_map == flare_pcx) // picked up the flare
{
item_ptr = flare_pan;
}
item_ptr.pos_x = 960; // set the proper position for the item
item_ptr.pos_y = 165; // on the x and y axis
item_ptr.visible = on; // and make it visible
item1_placed = 1; // slot1 is occupied
mouse_map = pointer_pcx; // restore the initial bitmap (the arrow)
show_pointer = 0; // hide the mouse pointer
}
Diese Funktion wird aufgerufen, wenn der Spieler den ersten Panel Slot anklickt; sie prüft, ob der Spieler ein Objekt hält oder nicht (mouse_map == pointer_pcs) und ob der Slot bereits besetzt ist (item1_placed == 1). Falls mouse_map gleich shield_pcx ist, hat der Spieler das Schild aufgehoben, also wird das shield_pan Panel im Inventar angezeigt. Das Gleiche geschieht für die anderen Items, die immer bei x = 960 und y = 165 für den ersten Slot plaziert werden. Wir machen das korrekte Panel sichtbar (mit einem Panel Pointer namens item_ptr), markieren den Slot als belegt und stellen die Originalbitmap für die Maus wieder her (pointer_pcx). Schließlich wird die Maus verborgen, da wir sie im Moment nicht brauchen. Die anderen Slots haben ähnliche Funktionen.
action shield_init
{
my.enable_click = on;
my.event = pick_shield;
while (player == null) {wait (1);}
while (player.shield == 0) // wait until the player has got the shield
{
ent_cycle("idle", my.skill20); // play the "idle" animation
my.skill20 += 3 * time; // "idle" animation
speed
my.skill20 %= 100;
wait (1);
}
ent_remove(my);
}
function pick_shield()
{
if (mouse_map != pointer_pcx) {return;} // don't pick the item
if the mouse pointer isn't the cursor
if (vec_dist (player.x, my.x) > 100) {return;} // if the player
is far away ignore it
player.shield = 1; // the player has got the shield
show_pointer = 1; // show the mouse pointer
mouse_map = shield_pcx;
}
Das Schild reagiert auf Mausklicks mit der Eventfunktion “pick_shield”. Wir warten bis der Spieler im Level existiert und animieren das Schild mit der “idle”-Animation, bis der Spieler es aufhebt – dann entfernen wir das Model. Die Eventfunktion tut nichts, wenn der Mauszeiger nicht der Standard “pointer_pcx” ist; auf diese Weise kann nicht mehr als ein Item aufgehoben werden ohne im Inventar abgelegt zu werden.
Das Schild kann nicht aufgehoben werden, wenn der Spieler weiter als 100 Quants entfernt ist. Falls der Spieler das Schild hat, wird player.shield (ein anderer Name für player.skill1) auf 1 gesetzt, der Mauszeiger wird angezeigt und der Zeiger wird durch das Bild des schildes ersetzt. Unsere Funktionen place_item1() bis place_item4() erledigen den Rest.
function use_shield()
{
if (mouse_map != pointer_pcx) {return;} // use the shield only if the
pointer
is normal (not an item)
player.shield = 0; // remove the shield from the panel
if (shield_pan.pos_y == 165) // the shield was placed in the 1st slot?
{
item1_placed = 0; // allow the use of the first slot
for other items from now
on
}
if (shield_pan.pos_y == 270) // the shield was placed in the 2nd slot?
{
item2_placed = 0; // allow the use of the first slot
for other items from now
on
}
if (shield_pan.pos_y == 380) // the shield was placed in the 3rd slot?
{
item3_placed = 0; // allow the use of the first slot
for other items from now
on
}
if (shield_pan.pos_y == 485) // the shield was placed in the 4th slot?
{
item4_placed = 0; // allow the use of the first slot
for other items from now
on
}
shield_pan.visible = off;
ent_create(shield_mdl, player.x, attach_shield);
shield_on = 1;
}
Die obige Funktion läuft, wenn der Spieler ein Inventarpanel mit dem normalen Mauszeiger anklickt. Das Schild wird vom Panel entfernt und die entsprechende Variable wird auf 0 zurückgesetzt; dies markiert den Slot als frei und erlaubt es dem Spieler ein anderes Item dort abzulegen. Das Schild wird erstellt und am Spieler angezeigt, was Aufgabe der folgenden Funktion ist:
function attach_shield()
{
proc_late();
my.passable = on;
my.metal = on;
my.albedo = 0;
while(player != null)
{
vec_set(my.x, player.x);
vec_set(my.pan, player.pan);
my.frame = player.frame;
my.next_frame = player.next_frame;
wait(1);
}
}
Das Schildmodel ist passable und existiert, solange es den Spieler gibt. Das Schild übernimmt Position, Ausrichtung und Animationsframe des Spielers. Die Funktionen für die anderen Items sind ähnlich, der einizig signifikante Unterschied liegt in der folgenden Funktion:
function attach_flare()
{
proc_late();
my.passable = on;
my.scale_x = 0.45; // decrease the size of the sprite
my.scale_y = my.scale_x;
my.albedo = 0;
my.ambient = 100;
while(mace != null) // show the flare for as long as the mace exists
{
vec_for_vertex(mace_tip, mace, 41); // get the coordinates
for the mace's tip
my.x = mace_tip.x;
my.y = mace_tip.y;
my.z = mace_tip.z;
my.roll += 3 * time;
wait(1);
}
ent_remove(my);
}
Diese Funktion plaziert ein Sprite an der Spitze des Streitkolbens, was dem Spieler zeigt, dass der Blitzschlagzauber aktiv ist, wenn er genug Mana hat. Mit einer Schleife ermitteln wir die Koordinaten der Spitze (Vertex 41) in jedem Frame und plazieren das Sprite entsprechend.
Einige ältere Morrowingskripte wurden überarbeitet; sehen wir uns die größten Änderungen der player.wdl an:
if
((mace_on == 1) && (mouse_mode == 0) && (mouse_left
== 1))
{
my.skill48 = 0;
vec_set(trace_coords.x, vector(100, 0, 0));
vec_rotate(trace_coords.x, player.pan);
vec_add(trace_coords.x, player.x);
trace_mode = ignore_me + ignore_passable + use_box;
if ((trace(player.x, trace_coords.x) > 0) && (you != null))
{
snd_play(fire_wav, 50, 0);
you.health -= 35;
}
while (my.skill48 < 100)
{
ent_cycle("attack", my.skill48);
my.skill48 += 5 * time;
wait (1);
}
}
Dieser Codeteil wurde in die While-Schleife des Spielers integriert. Wenn er den Streitkolben aufgehoben und angelegt hat, der Mauszeiger verborgen ist und die linke Maustaste gedrückt wude, tracen wir 100 Quants vor den Spieler und ignorieren dabei den Streitkolben und das Schild (welche passable sind) und falls wir eine Entity (keine Wand) getroffen haben, ertönt ein geräusch und wir verringern die Gesundheit der getroffenen Entity um 35 Punkte. Schließlich wird noch die “attack” Animation abgespielt.
//
got the mace and the pointer is hidden and the RMB is pressed and the flare
is used and the player has enough mana?
if ((mace_on == 1) && (mouse_mode == 0) && (mouse_right
== 1) && (flare_on
== 1) && (player.mana > 70))
{
my.skill48 = 0;
player.mana = 0;
snd_play(flare_wav, 50, 0); // play the lightning
sound
while (my.skill48 < 100)
{
vec_set(trace_coords.x, vector(500,
0, 0)); // trace 500 quants in front of the player (any skeleton will be alerted
at 600
quants)
vec_rotate(trace_coords.x,
player.pan); // rotate "trace_coords" in
the direction (angles) given by the player
vec_add(trace_coords.x, player.x);
// add the resulting vector to player's position
trace_mode = ignore_me + ignore_passable
+ use_box; // ignore the mace and the shield
vec_set (line_start.x, mace_tip.x);
vec_set (line_end.x, trace_coords.x);
lightning_effect();
if ((trace(player.x, trace_coords.x) > 0) && (you
!= null)) // hit something?
{
you.health
-= 100; // kill the enemy
}
ent_cycle("attack",
100); // play the last "attack" animation
frame
my.skill48 += 5 * time;
wait (1);
}
}
Wenn der Spieler den Streitkolben gefunden und angelegt hat (mace_on == 1), die rechte Maustaste gedrückt wurde, der Blitz angelegt ist und der Spieler genug Mana hat, wird sein Mana auf 0 reduziert, ein Geräusch ertönt und wir tracen 500 Quants nach vorn. Der Blitzeffekt wird mit der Spitze des Streitkolbens als Startpunkt und dem errechneten Ziel als Endpunkt generiert. Wird etwas getroffen, ziehen wir 100 Gesundheitspunkte ab, was jedes Skelett auf der Stelle umbringt. Der Spieler bekommt den letzten "attack” Frame und wir stellen sicher, dass der Effekt 1 – 2 Sekunden dauert, bis skill48 größer ist als 100.
Der Blitzeffekt ist in der particles.wdl zu finden; er ist eine verbesserte Version der alten particle_line Funktion. Der Code erzeugt mehrere Blitzsegmente von jeweils 100 Quants Länge und plaziert alle 5 Quants ein Partikel. Diese sind für einen Frame zu sehen; ändern Sie diese Werte, bis der Effekt Ihnen gut gefällt.
Viel Spaß!